From d6c3cf3b8ff40dd7bdf12665268ac757234f95c0 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 15:40:04 +0200 Subject: [PATCH 001/164] Better frequency for people memories --- .../apps/photos/lib/models/memories/memories_cache.dart | 2 +- .../lib/models/memories/smart_memory_constants.dart | 3 ++- .../apps/photos/lib/services/smart_memories_service.dart | 9 ++++++++- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index f45282d30c..622c3b8485 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -9,7 +9,7 @@ import "package:photos/models/memories/smart_memory.dart"; import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; -const kPersonShowTimeout = Duration(days: 7 * 10); +const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); const kPersonAndTypeShowTimeout = Duration(days: 7 * 26); const kClipShowTimeout = Duration(days: 3 * 10); const kTripShowTimeout = Duration(days: 7 * 25); diff --git a/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart b/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart index 2d9885f90e..35a8b4130c 100644 --- a/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart +++ b/mobile/apps/photos/lib/models/memories/smart_memory_constants.dart @@ -1,4 +1,5 @@ // Constants for computing smart memories -const kMemoriesUpdateFrequency = Duration(days: 3); +const kMemoriesUpdateFrequencyDays = 3; +const kMemoriesUpdateFrequency = Duration(days: kMemoriesUpdateFrequencyDays); const kMemoriesMargin = Duration(days: 1); const kDayItself = Duration(days: 1); diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index ce20f14cd2..9875df1402 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -422,6 +422,7 @@ class SmartMemoriesService { .map((p) => p.remoteID) .toList(); orderedImportantPersonsID.shuffle(Random()); + final amountOfPersons = orderedImportantPersonsID.length; w?.log('orderedImportantPersonsID setup'); // Check if the user has assignmed "me" @@ -709,6 +710,12 @@ class SmartMemoriesService { w?.log('relevancy setup'); // Loop through the people (and memory types) and add based on rotation + final shownPersonTimeout = Duration( + days: min( + kPersonShowTimeout.inDays, + max(1, amountOfPersons) * kMemoriesUpdateFrequencyDays, + ), + ); peopleRotationLoop: for (final personID in orderedImportantPersonsID) { for (final memory in memoryResults) { @@ -721,7 +728,7 @@ class SmartMemoriesService { final shownDate = DateTime.fromMicrosecondsSinceEpoch(shownLog.lastTimeShown); final bool seenPersonRecently = - currentTime.difference(shownDate) < kPersonShowTimeout; + currentTime.difference(shownDate) < shownPersonTimeout; if (seenPersonRecently) continue peopleRotationLoop; } if (personToMemories[personID] == null) continue peopleRotationLoop; From a6b29525d2979fe990ad8399c5adce79937600ab Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 16:51:49 +0200 Subject: [PATCH 002/164] Better frequency for people type --- .../photos/lib/models/memories/memories_cache.dart | 2 -- .../photos/lib/services/smart_memories_service.dart | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index 622c3b8485..1c1c01b5bc 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -10,13 +10,11 @@ import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); -const kPersonAndTypeShowTimeout = Duration(days: 7 * 26); const kClipShowTimeout = Duration(days: 3 * 10); const kTripShowTimeout = Duration(days: 7 * 25); final maxShowTimeout = [ kPersonShowTimeout, - kPersonAndTypeShowTimeout, kTripShowTimeout, ].reduce((value, element) => value > element ? value : element) * 3; diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index 9875df1402..9583bbc38d 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -716,6 +716,8 @@ class SmartMemoriesService { max(1, amountOfPersons) * kMemoriesUpdateFrequencyDays, ), ); + final shownPersonAndTypeTimeout = + Duration(days: shownPersonTimeout.inDays * 2); peopleRotationLoop: for (final personID in orderedImportantPersonsID) { for (final memory in memoryResults) { @@ -733,6 +735,8 @@ class SmartMemoriesService { } if (personToMemories[personID] == null) continue peopleRotationLoop; int added = 0; + final amountOfMemoryTypesForPerson = personToMemories[personID]!.length; + final bool manyMemoryTypes = amountOfMemoryTypesForPerson > 2; potentialMemoryLoop: for (final memoriesForCategory in personToMemories[personID]!.values) { PeopleMemory potentialMemory = memoriesForCategory.first; @@ -755,8 +759,10 @@ class SmartMemoriesService { final shownTypeDate = DateTime.fromMicrosecondsSinceEpoch(shownLog.lastTimeShown); final bool seenPersonTypeRecently = - currentTime.difference(shownTypeDate) < kPersonAndTypeShowTimeout; - if (seenPersonTypeRecently) continue potentialMemoryLoop; + currentTime.difference(shownTypeDate) < shownPersonAndTypeTimeout; + if (manyMemoryTypes && seenPersonTypeRecently) { + continue potentialMemoryLoop; + } } memoryResults.add(potentialMemory); added++; From 86967175f7794482cb247833a289e85909cc6511 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 31 Jul 2025 16:58:26 +0200 Subject: [PATCH 003/164] Better values --- mobile/apps/photos/lib/models/memories/memories_cache.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/models/memories/memories_cache.dart b/mobile/apps/photos/lib/models/memories/memories_cache.dart index 1c1c01b5bc..f49a1a1014 100644 --- a/mobile/apps/photos/lib/models/memories/memories_cache.dart +++ b/mobile/apps/photos/lib/models/memories/memories_cache.dart @@ -10,8 +10,8 @@ import "package:photos/models/memories/smart_memory_constants.dart"; import "package:photos/models/memories/trip_memory.dart"; const kPersonShowTimeout = Duration(days: 16 * kMemoriesUpdateFrequencyDays); -const kClipShowTimeout = Duration(days: 3 * 10); -const kTripShowTimeout = Duration(days: 7 * 25); +const kClipShowTimeout = Duration(days: 10 * kMemoriesUpdateFrequencyDays); +const kTripShowTimeout = Duration(days: 50 * kMemoriesUpdateFrequencyDays); final maxShowTimeout = [ kPersonShowTimeout, From 8304bca71c18fc8c58ee08fce3a4587f07f52824 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 4 Aug 2025 14:19:04 +0530 Subject: [PATCH 004/164] empty list error --- .../lib/ui/viewer/people/person_gallery_suggestion.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart index 7311e81de5..854c69e4c0 100644 --- a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart +++ b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart @@ -101,7 +101,9 @@ class _PersonGallerySuggestionState extends State } else { suggestions = await ClusterFeedbackService.instance .getAllLargePersonSuggestions(); - person = suggestions.first.person; + if (suggestions.isNotEmpty) { + person = suggestions.first.person; + } } if (suggestions.isNotEmpty && mounted) { From a41b7f5535bda1324178153a4357f2dc35774992 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Mon, 4 Aug 2025 14:24:36 +0530 Subject: [PATCH 005/164] null check error --- .../lib/ui/viewer/people/person_gallery_suggestion.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart index 854c69e4c0..59aa4a869e 100644 --- a/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart +++ b/mobile/apps/photos/lib/ui/viewer/people/person_gallery_suggestion.dart @@ -124,8 +124,8 @@ class _PersonGallerySuggestionState extends State } if (mounted && _fadeController != null && _slideController != null) { - unawaited(_fadeController!.forward()); - unawaited(_slideController!.forward()); + unawaited(_fadeController?.forward()); + unawaited(_slideController?.forward()); } unawaited(_precomputeNextSuggestions()); @@ -385,8 +385,8 @@ class _PersonGallerySuggestionState extends State Future _animateIn() async { if (mounted && _fadeController != null && _slideController != null) { - _slideController!.reset(); - _fadeController!.reset(); + _slideController?.reset(); + _fadeController?.reset(); await Future.wait([ _fadeController!.forward(), _slideController!.forward(), From 9d76ccc17303cbd452d54c18aaa738fae20bdda3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 15:30:30 +0530 Subject: [PATCH 006/164] init config --- mobile/packages/.gitignore | 1 + mobile/packages/configuration/README.md | 21 ++ .../configuration/lib/configuration.dart | 7 + mobile/packages/configuration/pubspec.lock | 205 ++++++++++++++++++ mobile/packages/configuration/pubspec.yaml | 18 ++ 5 files changed, 252 insertions(+) create mode 100644 mobile/packages/.gitignore create mode 100644 mobile/packages/configuration/README.md create mode 100644 mobile/packages/configuration/lib/configuration.dart create mode 100644 mobile/packages/configuration/pubspec.lock create mode 100644 mobile/packages/configuration/pubspec.yaml diff --git a/mobile/packages/.gitignore b/mobile/packages/.gitignore new file mode 100644 index 0000000000..cb3e7cf51b --- /dev/null +++ b/mobile/packages/.gitignore @@ -0,0 +1 @@ +.dart_tool/ diff --git a/mobile/packages/configuration/README.md b/mobile/packages/configuration/README.md new file mode 100644 index 0000000000..59d435360f --- /dev/null +++ b/mobile/packages/configuration/README.md @@ -0,0 +1,21 @@ +# Configuration + +A Flutter package for shared configuration across ente apps. + +## Usage + +Import the package and call the init method from your app's main.dart: + +```dart +import 'package:configuration/configuration.dart'; + +void main() async { + await Configuration.init(); + // ... rest of your app initialization +} +``` + +## Features + +- Shared configuration initialization +- Common setup logic for ente apps diff --git a/mobile/packages/configuration/lib/configuration.dart b/mobile/packages/configuration/lib/configuration.dart new file mode 100644 index 0000000000..cd3ad1803b --- /dev/null +++ b/mobile/packages/configuration/lib/configuration.dart @@ -0,0 +1,7 @@ +library configuration; + +class Configuration { + static Future init() async { + // TODO + } +} diff --git a/mobile/packages/configuration/pubspec.lock b/mobile/packages/configuration/pubspec.lock new file mode 100644 index 0000000000..6bbc3fcd93 --- /dev/null +++ b/mobile/packages/configuration/pubspec.lock @@ -0,0 +1,205 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/configuration/pubspec.yaml b/mobile/packages/configuration/pubspec.yaml new file mode 100644 index 0000000000..4883fdb8dd --- /dev/null +++ b/mobile/packages/configuration/pubspec.yaml @@ -0,0 +1,18 @@ +name: configuration +description: A Flutter package for shared configuration across ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From 62d73117800fb37c2d099cac0aa64b102e52e222 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 15:35:07 +0530 Subject: [PATCH 007/164] ignore surprises --- mobile/apps/.gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 mobile/apps/.gitignore diff --git a/mobile/apps/.gitignore b/mobile/apps/.gitignore new file mode 100644 index 0000000000..201fd57dc6 --- /dev/null +++ b/mobile/apps/.gitignore @@ -0,0 +1 @@ +surprise/ From d88df36c3d3ce4777b5035561f60851b35e8e81f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 17:43:11 +0530 Subject: [PATCH 008/164] Packages --- mobile/packages/.gitignore | 1 + mobile/packages/base/analysis_options.yaml | 10 + mobile/packages/base/lib/ente_base.dart | 6 + mobile/packages/base/lib/models/database.dart | 3 + .../base/lib/models/key_attributes.dart | 102 ++++ .../base/lib/models/key_gen_result.dart | 12 + .../lib/models/private_key_attributes.dart | 7 + mobile/packages/base/pubspec.lock | 205 ++++++++ mobile/packages/base/pubspec.yaml | 18 + .../configuration/lib/configuration.dart | 403 +++++++++++++- .../packages/configuration/lib/constants.dart | 5 + mobile/packages/configuration/pubspec.lock | 495 +++++++++++++++++- mobile/packages/configuration/pubspec.yaml | 15 + mobile/packages/events/analysis_options.yaml | 10 + mobile/packages/events/lib/ente_events.dart | 13 + mobile/packages/events/lib/event_bus.dart | 5 + .../lib/models/collections_updated_event.dart | 3 + .../lib/models/endpoint_updated_event.dart | 3 + mobile/packages/events/lib/models/event.dart | 1 + .../events/lib/models/signed_in_event.dart | 3 + .../events/lib/models/signed_out_event.dart | 3 + .../lib/models/trigger_logout_event.dart | 3 + .../models/user_details_changed_event.dart | 3 + mobile/packages/events/pubspec.lock | 213 ++++++++ mobile/packages/events/pubspec.yaml | 19 + mobile/packages/logging/analysis_options.yaml | 10 + mobile/packages/logging/lib/logging.dart | 3 + .../logging/lib/src/super_logging.dart | 402 ++++++++++++++ .../logging/lib/src/tunneled_transport.dart | 140 +++++ mobile/packages/logging/pubspec.lock | 474 +++++++++++++++++ mobile/packages/logging/pubspec.yaml | 28 + 31 files changed, 2614 insertions(+), 4 deletions(-) create mode 100644 mobile/packages/base/analysis_options.yaml create mode 100644 mobile/packages/base/lib/ente_base.dart create mode 100644 mobile/packages/base/lib/models/database.dart create mode 100644 mobile/packages/base/lib/models/key_attributes.dart create mode 100644 mobile/packages/base/lib/models/key_gen_result.dart create mode 100644 mobile/packages/base/lib/models/private_key_attributes.dart create mode 100644 mobile/packages/base/pubspec.lock create mode 100644 mobile/packages/base/pubspec.yaml create mode 100644 mobile/packages/configuration/lib/constants.dart create mode 100644 mobile/packages/events/analysis_options.yaml create mode 100644 mobile/packages/events/lib/ente_events.dart create mode 100644 mobile/packages/events/lib/event_bus.dart create mode 100644 mobile/packages/events/lib/models/collections_updated_event.dart create mode 100644 mobile/packages/events/lib/models/endpoint_updated_event.dart create mode 100644 mobile/packages/events/lib/models/event.dart create mode 100644 mobile/packages/events/lib/models/signed_in_event.dart create mode 100644 mobile/packages/events/lib/models/signed_out_event.dart create mode 100644 mobile/packages/events/lib/models/trigger_logout_event.dart create mode 100644 mobile/packages/events/lib/models/user_details_changed_event.dart create mode 100644 mobile/packages/events/pubspec.lock create mode 100644 mobile/packages/events/pubspec.yaml create mode 100644 mobile/packages/logging/analysis_options.yaml create mode 100644 mobile/packages/logging/lib/logging.dart create mode 100644 mobile/packages/logging/lib/src/super_logging.dart create mode 100644 mobile/packages/logging/lib/src/tunneled_transport.dart create mode 100644 mobile/packages/logging/pubspec.lock create mode 100644 mobile/packages/logging/pubspec.yaml diff --git a/mobile/packages/.gitignore b/mobile/packages/.gitignore index cb3e7cf51b..009c454944 100644 --- a/mobile/packages/.gitignore +++ b/mobile/packages/.gitignore @@ -1 +1,2 @@ .dart_tool/ +.flutter-plugins-dependencies diff --git a/mobile/packages/base/analysis_options.yaml b/mobile/packages/base/analysis_options.yaml new file mode 100644 index 0000000000..609eb5d8aa --- /dev/null +++ b/mobile/packages/base/analysis_options.yaml @@ -0,0 +1,10 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - always_put_required_named_parameters_first + - avoid_print + - prefer_const_constructors + - prefer_const_literals_to_create_immutables + - use_key_in_widget_constructors diff --git a/mobile/packages/base/lib/ente_base.dart b/mobile/packages/base/lib/ente_base.dart new file mode 100644 index 0000000000..6f9e6961da --- /dev/null +++ b/mobile/packages/base/lib/ente_base.dart @@ -0,0 +1,6 @@ +library ente_base; + +export 'models/database.dart'; +export 'models/key_attributes.dart'; +export 'models/key_gen_result.dart'; +export 'models/private_key_attributes.dart'; diff --git a/mobile/packages/base/lib/models/database.dart b/mobile/packages/base/lib/models/database.dart new file mode 100644 index 0000000000..61216d45e1 --- /dev/null +++ b/mobile/packages/base/lib/models/database.dart @@ -0,0 +1,3 @@ +abstract class EnteBaseDatabase { + Future clearTable(); +} diff --git a/mobile/packages/base/lib/models/key_attributes.dart b/mobile/packages/base/lib/models/key_attributes.dart new file mode 100644 index 0000000000..8b8ec3990f --- /dev/null +++ b/mobile/packages/base/lib/models/key_attributes.dart @@ -0,0 +1,102 @@ +import 'dart:convert'; + +class KeyAttributes { + final String kekSalt; + final String encryptedKey; + final String keyDecryptionNonce; + final String publicKey; + final String encryptedSecretKey; + final String secretKeyDecryptionNonce; + final int memLimit; + final int opsLimit; + final String masterKeyEncryptedWithRecoveryKey; + final String masterKeyDecryptionNonce; + final String recoveryKeyEncryptedWithMasterKey; + final String recoveryKeyDecryptionNonce; + + KeyAttributes( + this.kekSalt, + this.encryptedKey, + this.keyDecryptionNonce, + this.publicKey, + this.encryptedSecretKey, + this.secretKeyDecryptionNonce, + this.memLimit, + this.opsLimit, + this.masterKeyEncryptedWithRecoveryKey, + this.masterKeyDecryptionNonce, + this.recoveryKeyEncryptedWithMasterKey, + this.recoveryKeyDecryptionNonce, + ); + + Map toMap() { + return { + 'kekSalt': kekSalt, + 'encryptedKey': encryptedKey, + 'keyDecryptionNonce': keyDecryptionNonce, + 'publicKey': publicKey, + 'encryptedSecretKey': encryptedSecretKey, + 'secretKeyDecryptionNonce': secretKeyDecryptionNonce, + 'memLimit': memLimit, + 'opsLimit': opsLimit, + 'masterKeyEncryptedWithRecoveryKey': masterKeyEncryptedWithRecoveryKey, + 'masterKeyDecryptionNonce': masterKeyDecryptionNonce, + 'recoveryKeyEncryptedWithMasterKey': recoveryKeyEncryptedWithMasterKey, + 'recoveryKeyDecryptionNonce': recoveryKeyDecryptionNonce, + }; + } + + factory KeyAttributes.fromMap(Map map) { + return KeyAttributes( + map['kekSalt'], + map['encryptedKey'], + map['keyDecryptionNonce'], + map['publicKey'], + map['encryptedSecretKey'], + map['secretKeyDecryptionNonce'], + map['memLimit'], + map['opsLimit'], + map['masterKeyEncryptedWithRecoveryKey'], + map['masterKeyDecryptionNonce'], + map['recoveryKeyEncryptedWithMasterKey'], + map['recoveryKeyDecryptionNonce'], + ); + } + + String toJson() => json.encode(toMap()); + + factory KeyAttributes.fromJson(String source) => + KeyAttributes.fromMap(json.decode(source)); + + KeyAttributes copyWith({ + String? kekSalt, + String? encryptedKey, + String? keyDecryptionNonce, + String? publicKey, + String? encryptedSecretKey, + String? secretKeyDecryptionNonce, + int? memLimit, + int? opsLimit, + String? masterKeyEncryptedWithRecoveryKey, + String? masterKeyDecryptionNonce, + String? recoveryKeyEncryptedWithMasterKey, + String? recoveryKeyDecryptionNonce, + }) { + return KeyAttributes( + kekSalt ?? this.kekSalt, + encryptedKey ?? this.encryptedKey, + keyDecryptionNonce ?? this.keyDecryptionNonce, + publicKey ?? this.publicKey, + encryptedSecretKey ?? this.encryptedSecretKey, + secretKeyDecryptionNonce ?? this.secretKeyDecryptionNonce, + memLimit ?? this.memLimit, + opsLimit ?? this.opsLimit, + masterKeyEncryptedWithRecoveryKey ?? + this.masterKeyEncryptedWithRecoveryKey, + masterKeyDecryptionNonce ?? this.masterKeyDecryptionNonce, + recoveryKeyEncryptedWithMasterKey ?? + this.recoveryKeyEncryptedWithMasterKey, + recoveryKeyDecryptionNonce ?? this.recoveryKeyDecryptionNonce, + ); + } +} diff --git a/mobile/packages/base/lib/models/key_gen_result.dart b/mobile/packages/base/lib/models/key_gen_result.dart new file mode 100644 index 0000000000..27dd062229 --- /dev/null +++ b/mobile/packages/base/lib/models/key_gen_result.dart @@ -0,0 +1,12 @@ +import "dart:typed_data"; + +import 'key_attributes.dart'; +import 'private_key_attributes.dart'; + +class KeyGenResult { + final KeyAttributes keyAttributes; + final PrivateKeyAttributes privateKeyAttributes; + final Uint8List loginKey; + + KeyGenResult(this.keyAttributes, this.privateKeyAttributes, this.loginKey); +} diff --git a/mobile/packages/base/lib/models/private_key_attributes.dart b/mobile/packages/base/lib/models/private_key_attributes.dart new file mode 100644 index 0000000000..c92f017fc6 --- /dev/null +++ b/mobile/packages/base/lib/models/private_key_attributes.dart @@ -0,0 +1,7 @@ +class PrivateKeyAttributes { + final String key; + final String recoveryKey; + final String secretKey; + + PrivateKeyAttributes(this.key, this.recoveryKey, this.secretKey); +} diff --git a/mobile/packages/base/pubspec.lock b/mobile/packages/base/pubspec.lock new file mode 100644 index 0000000000..6bbc3fcd93 --- /dev/null +++ b/mobile/packages/base/pubspec.lock @@ -0,0 +1,205 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/base/pubspec.yaml b/mobile/packages/base/pubspec.yaml new file mode 100644 index 0000000000..04aeb70512 --- /dev/null +++ b/mobile/packages/base/pubspec.yaml @@ -0,0 +1,18 @@ +name: ente_base +description: A base Flutter package containing shared models and types for ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/mobile/packages/configuration/lib/configuration.dart b/mobile/packages/configuration/lib/configuration.dart index cd3ad1803b..bfe8c5ef43 100644 --- a/mobile/packages/configuration/lib/configuration.dart +++ b/mobile/packages/configuration/lib/configuration.dart @@ -1,7 +1,406 @@ library configuration; +import 'dart:convert'; +import 'dart:io' as io; +import 'dart:typed_data'; + +import 'package:bip39/bip39.dart' as bip39; +import 'package:configuration/constants.dart'; +import 'package:ente_base/models/database.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; +import 'package:ente_base/models/private_key_attributes.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/endpoint_updated_event.dart'; +import 'package:ente_events/models/signed_in_event.dart'; +import 'package:ente_events/models/signed_out_event.dart'; +import 'package:ente_logging/logging.dart'; +import 'package:flutter_secure_storage/flutter_secure_storage.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tuple/tuple.dart'; + class Configuration { - static Future init() async { - // TODO + Configuration._privateConstructor(); + + static final Configuration instance = Configuration._privateConstructor(); + static const endpoint = String.fromEnvironment( + "endpoint", + defaultValue: kDefaultProductionEndpoint, + ); + static const emailKey = "email"; + static const keyAttributesKey = "key_attributes"; + + static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time"; + static const keyKey = "key"; + static const secretKeyKey = "secret_key"; + static const authSecretKeyKey = "auth_secret_key"; + static const offlineAuthSecretKey = "offline_auth_secret_key"; + static const tokenKey = "token"; + static const encryptedTokenKey = "encrypted_token"; + static const userIDKey = "user_id"; + static const endPointKey = "endpoint"; + + final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds; + + static final _logger = Logger("Configuration"); + + String? _cachedToken; + late SharedPreferences _preferences; + String? _key; + String? _secretKey; + late FlutterSecureStorage _secureStorage; + late String _documentsDirectory; + late String _cacheDirectory; + late String _tempDocumentsDirPath; + late List _databases; + + String? _volatilePassword; + + Future init(List dbs) async { + _databases = dbs; + _documentsDirectory = (await getApplicationDocumentsDirectory()).path; + _tempDocumentsDirPath = "$_documentsDirectory/temp/"; + _preferences = await SharedPreferences.getInstance(); + _secureStorage = const FlutterSecureStorage( + iOptions: IOSOptions( + accessibility: KeychainAccessibility.first_unlock_this_device, + ), + ); + final tempDirectory = io.Directory(_tempDocumentsDirPath); + try { + final currentTime = DateTime.now().microsecondsSinceEpoch; + if (tempDirectory.existsSync() && + (_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) < + (currentTime - kTempFolderDeletionTimeBuffer)) { + await tempDirectory.delete(recursive: true); + await _preferences.setInt(lastTempFolderClearTimeKey, currentTime); + _logger.info("Cleared temp folder"); + } else { + _logger.info("Skipping temp folder clear"); + } + } catch (e) { + _logger.warning(e); + } + tempDirectory.createSync(recursive: true); + + _cacheDirectory = "$_documentsDirectory/cache/"; + if (!io.Directory(_cacheDirectory).existsSync()) { + io.Directory(_cacheDirectory).createSync(recursive: true); + } + } + + Future logout({bool autoLogout = false}) async { + await _preferences.clear(); + _secureStorage.deleteAll(); + for (final db in _databases) { + await db.clearTable(); + } + _key = null; + _cachedToken = null; + _secretKey = null; + Bus.instance.fire(SignedOutEvent()); + } + + Future generateKey(String password) async { + // Create a master key + final masterKey = CryptoUtil.generateKey(); + + // Create a recovery key + final recoveryKey = CryptoUtil.generateKey(); + + // Encrypt master key and recovery key with each other + final encryptedMasterKey = CryptoUtil.encryptSync(masterKey, recoveryKey); + final encryptedRecoveryKey = CryptoUtil.encryptSync(recoveryKey, masterKey); + + // Derive a key from the password that will be used to encrypt and + // decrypt the master key + final kekSalt = CryptoUtil.getSaltToDeriveKey(); + final derivedKeyResult = await CryptoUtil.deriveSensitiveKey( + utf8.encode(password), + kekSalt, + ); + final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key); + + // Encrypt the key with this derived key + final encryptedKeyData = + CryptoUtil.encryptSync(masterKey, derivedKeyResult.key); + + // Generate a public-private keypair and encrypt the latter + final keyPair = CryptoUtil.generateKeyPair(); + final encryptedSecretKeyData = + CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey); + + final attributes = KeyAttributes( + CryptoUtil.bin2base64(kekSalt), + CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), + CryptoUtil.bin2base64(encryptedKeyData.nonce!), + CryptoUtil.bin2base64(keyPair.publicKey), + CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!), + CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!), + derivedKeyResult.memLimit, + derivedKeyResult.opsLimit, + CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!), + CryptoUtil.bin2base64(encryptedMasterKey.nonce!), + CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!), + CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!), + ); + final privateAttributes = PrivateKeyAttributes( + CryptoUtil.bin2base64(masterKey), + CryptoUtil.bin2hex(recoveryKey), + CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()), + ); + return KeyGenResult(attributes, privateAttributes, loginKey); + } + + Future> getAttributesForNewPassword( + String password, + ) async { + // Get master key + final masterKey = getKey(); + + // Derive a key from the password that will be used to encrypt and + // decrypt the master key + final kekSalt = CryptoUtil.getSaltToDeriveKey(); + final derivedKeyResult = await CryptoUtil.deriveSensitiveKey( + utf8.encode(password), + kekSalt, + ); + final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key); + + // Encrypt the key with this derived key + final encryptedKeyData = + CryptoUtil.encryptSync(masterKey!, derivedKeyResult.key); + + final existingAttributes = getKeyAttributes(); + + final updatedAttributes = existingAttributes!.copyWith( + kekSalt: CryptoUtil.bin2base64(kekSalt), + encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), + keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!), + memLimit: derivedKeyResult.memLimit, + opsLimit: derivedKeyResult.opsLimit, + ); + return Tuple2(updatedAttributes, loginKey); + } + + // decryptSecretsAndGetLoginKey decrypts the master key and recovery key + // with the given password and save them in local secure storage. + // This method also returns the keyEncKey that can be used for performing + // SRP setup for existing users. + Future decryptSecretsAndGetKeyEncKey( + String password, + KeyAttributes attributes, { + Uint8List? keyEncryptionKey, + }) async { + _logger.info('Start decryptAndSaveSecrets'); + keyEncryptionKey ??= await CryptoUtil.deriveKey( + utf8.encode(password), + CryptoUtil.base642bin(attributes.kekSalt), + attributes.memLimit, + attributes.opsLimit, + ); + + _logger.info('user-key done'); + Uint8List key; + try { + key = CryptoUtil.decryptSync( + CryptoUtil.base642bin(attributes.encryptedKey), + keyEncryptionKey, + CryptoUtil.base642bin(attributes.keyDecryptionNonce), + ); + } catch (e) { + _logger.severe('master-key failed, incorrect password?', e); + throw Exception("Incorrect password"); + } + _logger.info("master-key done"); + await setKey(CryptoUtil.bin2base64(key)); + final secretKey = CryptoUtil.decryptSync( + CryptoUtil.base642bin(attributes.encryptedSecretKey), + key, + CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce), + ); + _logger.info("secret-key done"); + await setSecretKey(CryptoUtil.bin2base64(secretKey)); + final token = CryptoUtil.openSealSync( + CryptoUtil.base642bin(getEncryptedToken()!), + CryptoUtil.base642bin(attributes.publicKey), + secretKey, + ); + _logger.info('appToken done'); + await setToken( + CryptoUtil.bin2base64(token, urlSafe: true), + ); + return keyEncryptionKey; + } + + Future recover(String recoveryKey) async { + // check if user has entered mnemonic code + if (recoveryKey.contains(' ')) { + final split = recoveryKey.split(' '); + if (split.length != mnemonicKeyWordCount) { + String wordThatIsFollowedByEmptySpaceInSplit = ''; + for (int i = 0; i < split.length; i++) { + String word = split[i]; + if (word.isEmpty) { + wordThatIsFollowedByEmptySpaceInSplit = + '\n\nExtra space after word at position $i'; + break; + } + } + throw AssertionError( + '\nRecovery code should have $mnemonicKeyWordCount words, ' + 'found ${split.length} words instead.$wordThatIsFollowedByEmptySpaceInSplit', + ); + } + recoveryKey = bip39.mnemonicToEntropy(recoveryKey); + } + final attributes = getKeyAttributes(); + Uint8List masterKey; + try { + masterKey = await CryptoUtil.decrypt( + CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey), + CryptoUtil.hex2bin(recoveryKey), + CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce), + ); + } catch (e) { + _logger.severe(e); + rethrow; + } + await setKey(CryptoUtil.bin2base64(masterKey)); + final secretKey = CryptoUtil.decryptSync( + CryptoUtil.base642bin(attributes.encryptedSecretKey), + masterKey, + CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce), + ); + await setSecretKey(CryptoUtil.bin2base64(secretKey)); + final token = CryptoUtil.openSealSync( + CryptoUtil.base642bin(getEncryptedToken()!), + CryptoUtil.base642bin(attributes.publicKey), + secretKey, + ); + await setToken( + CryptoUtil.bin2base64(token, urlSafe: true), + ); + } + + String getHttpEndpoint() { + return _preferences.getString(endPointKey) ?? endpoint; + } + + Future setHttpEndpoint(String endpoint) async { + await _preferences.setString(endPointKey, endpoint); + Bus.instance.fire(EndpointUpdatedEvent()); + } + + String? getToken() { + _cachedToken ??= _preferences.getString(tokenKey); + return _cachedToken; + } + + bool isLoggedIn() { + return getToken() != null; + } + + Future setToken(String token) async { + _cachedToken = token; + await _preferences.setString(tokenKey, token); + Bus.instance.fire(SignedInEvent()); + } + + Future setEncryptedToken(String encryptedToken) async { + await _preferences.setString(encryptedTokenKey, encryptedToken); + } + + String? getEncryptedToken() { + return _preferences.getString(encryptedTokenKey); + } + + String? getEmail() { + return _preferences.getString(emailKey); + } + + Future setEmail(String email) async { + await _preferences.setString(emailKey, email); + } + + int? getUserID() { + return _preferences.getInt(userIDKey); + } + + Future setUserID(int userID) async { + await _preferences.setInt(userIDKey, userID); + } + + Future setKeyAttributes(KeyAttributes attributes) async { + await _preferences.setString(keyAttributesKey, attributes.toJson()); + } + + KeyAttributes? getKeyAttributes() { + final jsonValue = _preferences.getString(keyAttributesKey); + if (jsonValue == null) { + return null; + } else { + return KeyAttributes.fromJson(jsonValue); + } + } + + Future setKey(String key) async { + _key = key; + await _secureStorage.write( + key: keyKey, + value: key, + ); + } + + Future setSecretKey(String? secretKey) async { + _secretKey = secretKey; + await _secureStorage.write( + key: secretKeyKey, + value: secretKey, + ); + } + + Uint8List? getKey() { + return _key == null ? null : CryptoUtil.base642bin(_key!); + } + + Uint8List? getSecretKey() { + return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!); + } + + Uint8List getRecoveryKey() { + final keyAttributes = getKeyAttributes()!; + return CryptoUtil.decryptSync( + CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey), + getKey()!, + CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce), + ); + } + + // Caution: This directory is cleared on app start + String getTempDirectory() { + return _tempDocumentsDirPath; + } + + String getCacheDirectory() { + return _cacheDirectory; + } + + bool hasConfiguredAccount() { + return getToken() != null && _key != null; + } + + void setVolatilePassword(String volatilePassword) { + _volatilePassword = volatilePassword; + } + + void resetVolatilePassword() { + _volatilePassword = null; + } + + String? getVolatilePassword() { + return _volatilePassword; } } diff --git a/mobile/packages/configuration/lib/constants.dart b/mobile/packages/configuration/lib/constants.dart new file mode 100644 index 0000000000..7b28375d9f --- /dev/null +++ b/mobile/packages/configuration/lib/constants.dart @@ -0,0 +1,5 @@ +const kDefaultProductionEndpoint = 'https://api.ente.io'; + +// 256 bit key maps to 24 words +// https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#Generating_the_mnemonic +const mnemonicKeyWordCount = 24; diff --git a/mobile/packages/configuration/pubspec.lock b/mobile/packages/configuration/pubspec.lock index 6bbc3fcd93..11868bfed4 100644 --- a/mobile/packages/configuration/pubspec.lock +++ b/mobile/packages/configuration/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -9,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.13.0" + bip39: + dependency: "direct main" + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" boolean_selector: dependency: transitive description: @@ -41,6 +57,84 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + ente_base: + dependency: "direct main" + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: "direct main" + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" fake_async: dependency: transitive description: @@ -49,6 +143,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" flutter: dependency: "direct main" description: flutter @@ -62,11 +180,128 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -99,6 +334,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -123,6 +366,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" path: dependency: transitive description: @@ -131,11 +390,171 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" source_span: dependency: transitive description: @@ -144,6 +563,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" stack_trace: dependency: transitive description: @@ -168,6 +595,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" term_glyph: dependency: transitive description: @@ -184,6 +619,30 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.4" + tuple: + dependency: "direct main" + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" vector_math: dependency: transitive description: @@ -200,6 +659,38 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/configuration/pubspec.yaml b/mobile/packages/configuration/pubspec.yaml index 4883fdb8dd..df08596507 100644 --- a/mobile/packages/configuration/pubspec.yaml +++ b/mobile/packages/configuration/pubspec.yaml @@ -1,6 +1,7 @@ name: configuration description: A Flutter package for shared configuration across ente apps version: 1.0.0 +publish_to: none environment: sdk: ">=3.0.0 <4.0.0" @@ -9,6 +10,20 @@ environment: dependencies: flutter: sdk: flutter + bip39: ^1.0.6 + ente_base: + path: ../../packages/base + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../../packages/events + ente_logging: + path: ../../packages/logging + flutter_secure_storage: ^9.0.0 + path_provider: ^2.1.5 + shared_preferences: ^2.5.3 + tuple: ^2.0.2 dev_dependencies: flutter_test: diff --git a/mobile/packages/events/analysis_options.yaml b/mobile/packages/events/analysis_options.yaml new file mode 100644 index 0000000000..609eb5d8aa --- /dev/null +++ b/mobile/packages/events/analysis_options.yaml @@ -0,0 +1,10 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - always_put_required_named_parameters_first + - avoid_print + - prefer_const_constructors + - prefer_const_literals_to_create_immutables + - use_key_in_widget_constructors diff --git a/mobile/packages/events/lib/ente_events.dart b/mobile/packages/events/lib/ente_events.dart new file mode 100644 index 0000000000..023d166e49 --- /dev/null +++ b/mobile/packages/events/lib/ente_events.dart @@ -0,0 +1,13 @@ +library ente_events; + +// Export the event bus +export 'event_bus.dart'; + +// Export all event models +export 'models/event.dart'; +export 'models/signed_in_event.dart'; +export 'models/signed_out_event.dart'; +export 'models/trigger_logout_event.dart'; +export 'models/user_details_changed_event.dart'; +export 'models/endpoint_updated_event.dart'; +export 'models/collections_updated_event.dart'; diff --git a/mobile/packages/events/lib/event_bus.dart b/mobile/packages/events/lib/event_bus.dart new file mode 100644 index 0000000000..162a107439 --- /dev/null +++ b/mobile/packages/events/lib/event_bus.dart @@ -0,0 +1,5 @@ +import 'package:event_bus/event_bus.dart'; + +class Bus { + static final EventBus instance = EventBus(); +} diff --git a/mobile/packages/events/lib/models/collections_updated_event.dart b/mobile/packages/events/lib/models/collections_updated_event.dart new file mode 100644 index 0000000000..5fb0736a26 --- /dev/null +++ b/mobile/packages/events/lib/models/collections_updated_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class CollectionsUpdatedEvent extends Event {} diff --git a/mobile/packages/events/lib/models/endpoint_updated_event.dart b/mobile/packages/events/lib/models/endpoint_updated_event.dart new file mode 100644 index 0000000000..33c88d420d --- /dev/null +++ b/mobile/packages/events/lib/models/endpoint_updated_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class EndpointUpdatedEvent extends Event {} diff --git a/mobile/packages/events/lib/models/event.dart b/mobile/packages/events/lib/models/event.dart new file mode 100644 index 0000000000..61a42e7107 --- /dev/null +++ b/mobile/packages/events/lib/models/event.dart @@ -0,0 +1 @@ +class Event {} diff --git a/mobile/packages/events/lib/models/signed_in_event.dart b/mobile/packages/events/lib/models/signed_in_event.dart new file mode 100644 index 0000000000..15ae9cedc9 --- /dev/null +++ b/mobile/packages/events/lib/models/signed_in_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class SignedInEvent extends Event {} diff --git a/mobile/packages/events/lib/models/signed_out_event.dart b/mobile/packages/events/lib/models/signed_out_event.dart new file mode 100644 index 0000000000..84e7dc7822 --- /dev/null +++ b/mobile/packages/events/lib/models/signed_out_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class SignedOutEvent extends Event {} diff --git a/mobile/packages/events/lib/models/trigger_logout_event.dart b/mobile/packages/events/lib/models/trigger_logout_event.dart new file mode 100644 index 0000000000..48b068ffce --- /dev/null +++ b/mobile/packages/events/lib/models/trigger_logout_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class TriggerLogoutEvent extends Event {} diff --git a/mobile/packages/events/lib/models/user_details_changed_event.dart b/mobile/packages/events/lib/models/user_details_changed_event.dart new file mode 100644 index 0000000000..9cd352a2ac --- /dev/null +++ b/mobile/packages/events/lib/models/user_details_changed_event.dart @@ -0,0 +1,3 @@ +import 'event.dart'; + +class UserDetailsChangedEvent extends Event {} diff --git a/mobile/packages/events/pubspec.lock b/mobile/packages/events/pubspec.lock new file mode 100644 index 0000000000..2ee3344a78 --- /dev/null +++ b/mobile/packages/events/pubspec.lock @@ -0,0 +1,213 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + event_bus: + dependency: "direct main" + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/events/pubspec.yaml b/mobile/packages/events/pubspec.yaml new file mode 100644 index 0000000000..dc21a6ca95 --- /dev/null +++ b/mobile/packages/events/pubspec.yaml @@ -0,0 +1,19 @@ +name: ente_events +description: A Flutter package for event management using event_bus, extracted from the surprise app +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + event_bus: ^2.0.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: diff --git a/mobile/packages/logging/analysis_options.yaml b/mobile/packages/logging/analysis_options.yaml new file mode 100644 index 0000000000..609eb5d8aa --- /dev/null +++ b/mobile/packages/logging/analysis_options.yaml @@ -0,0 +1,10 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - always_put_required_named_parameters_first + - avoid_print + - prefer_const_constructors + - prefer_const_literals_to_create_immutables + - use_key_in_widget_constructors diff --git a/mobile/packages/logging/lib/logging.dart b/mobile/packages/logging/lib/logging.dart new file mode 100644 index 0000000000..9e31883c01 --- /dev/null +++ b/mobile/packages/logging/lib/logging.dart @@ -0,0 +1,3 @@ +export 'src/super_logging.dart'; +export 'src/tunneled_transport.dart'; +export 'package:logging/logging.dart'; diff --git a/mobile/packages/logging/lib/src/super_logging.dart b/mobile/packages/logging/lib/src/super_logging.dart new file mode 100644 index 0000000000..f78b48d758 --- /dev/null +++ b/mobile/packages/logging/lib/src/super_logging.dart @@ -0,0 +1,402 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:core'; +import 'dart:io'; + +import 'tunneled_transport.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:http/http.dart' as http; +import 'package:intl/intl.dart'; +import 'package:logging/logging.dart' as log; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:uuid/uuid.dart'; + +/// Type definition for functions that may return a value or Future +typedef FutureOrVoidCallback = dynamic Function(); + +extension SuperString on String { + Iterable chunked(int chunkSize) sync* { + var start = 0; + + while (true) { + final stop = start + chunkSize; + if (stop > length) break; + yield substring(start, stop); + start = stop; + } + + if (start < length) { + yield substring(start); + } + } +} + +extension SuperLogRecord on log.LogRecord { + String toPrettyString([String? extraLines]) { + final header = "[$loggerName] [$level] [$time]"; + + var msg = "$header $message"; + + if (error != null) { + msg += "\n⤷ type: ${error.runtimeType}\n⤷ error: $error"; + } + if (stackTrace != null) { + msg += "\n⤷ trace: $stackTrace"; + } + + for (var line in extraLines?.split('\n') ?? []) { + msg += '\n$header $line'; + } + + return msg; + } +} + +class LogConfig { + /// The DSN for a Sentry app. + /// This can be obtained from the Sentry apps's "settings > Client Keys (DSN)" page. + /// + /// Only logs containing errors are sent to sentry. + /// Errors can be caught using a try-catch block, like so: + /// + /// ``` + /// final logger = Logger("main"); + /// + /// try { + /// // do something dangerous here + /// } catch(e, trace) { + /// logger.info("Huston, we have a problem", e, trace); + /// } + /// ``` + /// + /// If this is [null], Sentry logger is completely disabled (default). + String? sentryDsn; + + String? tunnel; + + /// A built-in retry mechanism for sending errors to sentry. + /// + /// This parameter defines the time to wait for, before retrying. + Duration sentryRetryDelay; + + /// Path of the directory where log files will be stored. + /// + /// If this is [null], file logging is completely disabled (default). + /// + /// If this is an empty string (['']), + /// then a 'logs' directory will be created in [getTemporaryDirectory()]. + /// + /// A non-empty string will be treated as an explicit path to a directory. + /// + /// The chosen directory can be accessed using [SuperLogging.logFile.parent]. + String? logDirPath; + + /// The maximum number of log files inside [logDirPath]. + /// + /// One log file is created per day. + /// Older log files are deleted automatically. + int maxLogFiles; + + /// Whether to enable super logging features in debug mode. + /// + /// Sentry and file logging are typically not needed in debug mode, + /// where a complete logcat is available. + bool enableInDebugMode; + + /// If provided, super logging will invoke this function, and + /// any uncaught errors during its execution will be reported. + /// + /// Works by using [FlutterError.onError] and [runZoned]. + FutureOrVoidCallback? body; + + /// The date format for storing log files. + /// + /// `DateFormat('y-M-d')` by default. + DateFormat? dateFmt; + + String prefix; + + LogConfig({ + this.sentryDsn, + this.tunnel, + this.sentryRetryDelay = const Duration(seconds: 30), + this.logDirPath, + this.maxLogFiles = 10, + this.enableInDebugMode = false, + this.body, + this.dateFmt, + this.prefix = "", + }) { + dateFmt ??= DateFormat("y-M-d"); + } +} + +class SuperLogging { + /// The logger for SuperLogging + static final $ = log.Logger('ente_logging'); + + /// The current super logging configuration + static late LogConfig config; + + static late SharedPreferences _preferences; + + static const keyShouldReportErrors = "should_report_errors"; + + static const keyAnonymousUserID = "anonymous_user_id"; + + static Future main([LogConfig? appConfig]) async { + appConfig ??= LogConfig(); + SuperLogging.config = appConfig; + + WidgetsFlutterBinding.ensureInitialized(); + _preferences = await SharedPreferences.getInstance(); + + appVersion ??= await getAppVersion(); + + final enable = appConfig.enableInDebugMode || kReleaseMode; + sentryIsEnabled = + enable && appConfig.sentryDsn != null && shouldReportErrors(); + fileIsEnabled = enable && appConfig.logDirPath != null; + + if (fileIsEnabled) { + await setupLogDir(); + } + if (sentryIsEnabled) { + setupSentry().ignore(); + } + + log.Logger.root.level = log.Level.ALL; + log.Logger.root.onRecord.listen(onLogRecord); + + if (!enable) { + $.info("detected debug mode; sentry & file logging disabled."); + } + if (fileIsEnabled) { + $.info("log file for today: $logFile with prefix ${appConfig.prefix}"); + } + if (sentryIsEnabled) { + $.info("sentry uploader started"); + } + + if (appConfig.body == null) return; + + if (enable && sentryIsEnabled) { + await SentryFlutter.init( + (options) { + options.dsn = appConfig!.sentryDsn; + options.httpClient = http.Client(); + if (appConfig.tunnel != null) { + options.transport = + TunneledTransport(Uri.parse(appConfig.tunnel!), options); + } + }, + appRunner: () => appConfig!.body!(), + ); + } else { + await appConfig.body!(); + } + } + + static void setUserID(String userID) async { + if (config.sentryDsn != null) { + Sentry.configureScope((scope) => scope.setUser(SentryUser(id: userID))); + $.info("setting sentry user ID to: $userID"); + } + } + + static Future _sendErrorToSentry( + Object error, + StackTrace? stack, + ) async { + try { + await Sentry.captureException( + error, + stackTrace: stack, + ); + } catch (e) { + $.info('Sending report to sentry.io failed: $e'); + $.info('Original error: $error'); + } + } + + static String _lastExtraLines = ''; + + static Future onLogRecord(log.LogRecord rec) async { + // log misc info if it changed + String? extraLines = "app version: '$appVersion'\n"; + if (extraLines != _lastExtraLines) { + _lastExtraLines = extraLines; + } else { + extraLines = null; + } + + final str = "${config.prefix} ${rec.toPrettyString(extraLines)}"; + + // write to stdout + printLog(str); + + // push to log queue + if (fileIsEnabled) { + fileQueueEntries.add('$str\n'); + if (fileQueueEntries.length == 1) { + flushQueue(); + } + } + + // add error to sentry queue + if (sentryIsEnabled && rec.error != null) { + _sendErrorToSentry(rec.error!, null).ignore(); + } + } + + static final Queue fileQueueEntries = Queue(); + static bool isFlushing = false; + + static void flushQueue() async { + if (isFlushing || logFile == null) { + return; + } + isFlushing = true; + final entry = fileQueueEntries.removeFirst(); + await logFile!.writeAsString(entry, mode: FileMode.append, flush: true); + isFlushing = false; + if (fileQueueEntries.isNotEmpty) { + flushQueue(); + } + } + + // Logs on must be chunked or they get truncated otherwise + // See https://github.com/flutter/flutter/issues/22665 + static var logChunkSize = 800; + + static void printLog(String text) { + text.chunked(logChunkSize).forEach(debugPrint); + } + + /// A queue to be consumed by [setupSentry]. + static final sentryQueueControl = StreamController(); + + /// Whether sentry logging is currently enabled or not. + static bool sentryIsEnabled = false; + + static Future setupSentry() async { + $.info("Setting up sentry"); + SuperLogging.setUserID(await _getOrCreateAnonymousUserID()); + await for (final error in sentryQueueControl.stream.asBroadcastStream()) { + try { + await Sentry.captureException( + error, + ); + } catch (e) { + $.fine( + "sentry upload failed; will retry after ${config.sentryRetryDelay}", + ); + doSentryRetry(error); + } + } + } + + static void doSentryRetry(Error error) async { + await Future.delayed(config.sentryRetryDelay); + sentryQueueControl.add(error); + } + + static bool shouldReportErrors() { + if (_preferences.containsKey(keyShouldReportErrors)) { + return _preferences.getBool(keyShouldReportErrors)!; + } else { + return kDebugMode; + } + } + + static Future setShouldReportErrors(bool value) { + return _preferences.setBool(keyShouldReportErrors, value); + } + + static Future _getOrCreateAnonymousUserID() async { + if (!_preferences.containsKey(keyAnonymousUserID)) { + //ignore: prefer_const_constructors + await _preferences.setString(keyAnonymousUserID, Uuid().v4()); + } + return _preferences.getString(keyAnonymousUserID)!; + } + + /// The log file currently in use. + static File? logFile; + + /// Whether file logging is currently enabled or not. + static bool fileIsEnabled = false; + + static Future setupLogDir() async { + var dirPath = config.logDirPath; + + // choose [logDir] + if (dirPath == null || dirPath.isEmpty) { + final root = await getExternalStorageDirectory(); + dirPath = '${root!.path}/logs'; + } + + // create [logDir] + final dir = Directory(dirPath); + await dir.create(recursive: true); + + final files = []; + final dates = {}; + + // collect all log files with valid names + await for (final file in dir.list()) { + try { + final date = config.dateFmt!.parse(basename(file.path)); + dates[file as File] = date; + files.add(file); + } on Exception catch (_) {} + } + final nowTime = DateTime.now(); + + // delete old log files, if [maxLogFiles] is exceeded. + if (files.length > config.maxLogFiles) { + // sort files based on ascending order of date (older first) + files.sort( + (a, b) => (dates[a] ?? nowTime).compareTo((dates[b] ?? nowTime)), + ); + + final extra = files.length - config.maxLogFiles; + final toDelete = files.sublist(0, extra); + + for (final file in toDelete) { + try { + $.fine( + "deleting log file ${file.path}", + ); + await file.delete(); + } on Exception catch (_) {} + } + } + + logFile = File("$dirPath/${config.dateFmt!.format(DateTime.now())}.txt"); + } + + /// Current app version, obtained from package_info plugin. + /// + /// See: [getAppVersion] + static String? appVersion; + + static Future getAppVersion() async { + final pkgInfo = await PackageInfo.fromPlatform(); + return "${pkgInfo.version}+${pkgInfo.buildNumber}"; + } + + // disable sentry on f-droid. We need to make it opt-in preference + static Future isFDroidBuild() async { + if (!Platform.isAndroid) { + return false; + } + final pkgName = (await PackageInfo.fromPlatform()).packageName; + return pkgName.endsWith("fdroid"); + } +} diff --git a/mobile/packages/logging/lib/src/tunneled_transport.dart b/mobile/packages/logging/lib/src/tunneled_transport.dart new file mode 100644 index 0000000000..f9ffd5ad12 --- /dev/null +++ b/mobile/packages/logging/lib/src/tunneled_transport.dart @@ -0,0 +1,140 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:sentry/sentry.dart'; + +/// A transport is in charge of sending the event to the Sentry server. +class TunneledTransport implements Transport { + final Uri _tunnel; + final SentryOptions _options; + + final Dsn? _dsn; + + _CredentialBuilder? _credentialBuilder; + + final Map _headers; + + factory TunneledTransport(Uri tunnel, SentryOptions options) { + return TunneledTransport._(tunnel, options); + } + + TunneledTransport._(this._tunnel, this._options) + : _dsn = _options.dsn != null ? Dsn.parse(_options.dsn!) : null, + _headers = _buildHeaders( + _options.platformChecker.isWeb, + _options.sentryClientName, + ) { + _credentialBuilder = _CredentialBuilder( + _dsn, + _options.sentryClientName, + // ignore: invalid_use_of_internal_member + _options.clock, + ); + } + + @override + Future send(SentryEnvelope envelope) async { + final streamedRequest = await _createStreamedRequest(envelope); + final response = await _options.httpClient + .send(streamedRequest) + .then(Response.fromStream); + + if (response.statusCode != 200) { + // body guard to not log the error as it has performance impact to allocate + // the body String. + if (_options.debug) { + _options.logger( + SentryLevel.error, + 'API returned an error, statusCode = ${response.statusCode}, ' + 'body = ${response.body}', + ); + } + return const SentryId.empty(); + } else { + _options.logger( + SentryLevel.debug, + 'Envelope ${envelope.header.eventId ?? "--"} was sent successfully.', + ); + } + + final eventId = json.decode(response.body)['id']; + if (eventId == null) { + return null; + } + return SentryId.fromId(eventId); + } + + Future _createStreamedRequest( + SentryEnvelope envelope, + ) async { + final streamedRequest = StreamedRequest('POST', _tunnel); + envelope + .envelopeStream(_options) + .listen(streamedRequest.sink.add) + .onDone(streamedRequest.sink.close); + + streamedRequest.headers.addAll(_credentialBuilder!.configure(_headers)); + + return streamedRequest; + } +} + +class _CredentialBuilder { + final String _authHeader; + + final ClockProvider _clock; + + int get timestamp => _clock().millisecondsSinceEpoch; + + _CredentialBuilder._(String authHeader, ClockProvider clock) + : _authHeader = authHeader, + _clock = clock; + + factory _CredentialBuilder( + Dsn? dsn, + String sdkIdentifier, + ClockProvider clock, + ) { + final authHeader = _buildAuthHeader( + publicKey: dsn?.publicKey, + secretKey: dsn?.secretKey, + sdkIdentifier: sdkIdentifier, + ); + + return _CredentialBuilder._(authHeader, clock); + } + + static String _buildAuthHeader({ + String? publicKey, + String? secretKey, + String? sdkIdentifier, + }) { + var header = 'Sentry sentry_version=7, sentry_client=$sdkIdentifier, ' + 'sentry_key=$publicKey'; + + if (secretKey != null) { + header += ', sentry_secret=$secretKey'; + } + + return header; + } + + Map configure(Map headers) { + return headers + ..addAll( + { + 'X-Sentry-Auth': '$_authHeader, sentry_timestamp=$timestamp', + }, + ); + } +} + +Map _buildHeaders(bool isWeb, String sdkIdentifier) { + final headers = {'Content-Type': 'application/x-sentry-envelope'}; + // NOTE(lejard_h) overriding user agent on VM and Flutter not sure why + // for web it use browser user agent + if (!isWeb) { + headers['User-Agent'] = sdkIdentifier; + } + return headers; +} diff --git a/mobile/packages/logging/pubspec.lock b/mobile/packages/logging/pubspec.lock new file mode 100644 index 0000000000..4dd0697bf7 --- /dev/null +++ b/mobile/packages/logging/pubspec.lock @@ -0,0 +1,474 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + path: + dependency: "direct main" + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sentry: + dependency: "direct main" + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: "direct main" + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.27.0" diff --git a/mobile/packages/logging/pubspec.yaml b/mobile/packages/logging/pubspec.yaml new file mode 100644 index 0000000000..5dbf6ea650 --- /dev/null +++ b/mobile/packages/logging/pubspec.yaml @@ -0,0 +1,28 @@ +name: ente_logging +description: A Flutter package for logging functionality across ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + http: ^1.4.0 + intl: ^0.20.2 + logging: ^1.3.0 + package_info_plus: ^8.3.0 + path: ^1.9.0 + path_provider: ^2.1.5 + sentry: ^8.14.2 + sentry_flutter: ^8.14.2 + shared_preferences: ^2.5.3 + uuid: ^4.5.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From 3b9b886ae90a3ba65a83732e265e0369922589d9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 19:59:32 +0530 Subject: [PATCH 009/164] Refactor --- .../auth/lib/core/logging/super_logging.dart | 400 ------------------ .../lib/core/logging/tunneled_transport.dart | 140 ------ mobile/apps/auth/lib/main.dart | 3 +- .../ui/settings/general_section_widget.dart | 2 +- mobile/apps/auth/lib/utils/email_util.dart | 3 +- mobile/apps/auth/pubspec.lock | 7 + mobile/apps/auth/pubspec.yaml | 2 + 7 files changed, 12 insertions(+), 545 deletions(-) delete mode 100644 mobile/apps/auth/lib/core/logging/super_logging.dart delete mode 100644 mobile/apps/auth/lib/core/logging/tunneled_transport.dart diff --git a/mobile/apps/auth/lib/core/logging/super_logging.dart b/mobile/apps/auth/lib/core/logging/super_logging.dart deleted file mode 100644 index 1c5370c8e3..0000000000 --- a/mobile/apps/auth/lib/core/logging/super_logging.dart +++ /dev/null @@ -1,400 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; -import 'dart:core'; -import 'dart:io'; - -import 'package:ente_auth/core/logging/tunneled_transport.dart'; -import 'package:ente_auth/models/typedefs.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; -import 'package:http/http.dart' as http; -import 'package:intl/intl.dart'; -import 'package:logging/logging.dart'; -import 'package:package_info_plus/package_info_plus.dart'; -import 'package:path/path.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:uuid/uuid.dart'; - -extension SuperString on String { - Iterable chunked(int chunkSize) sync* { - var start = 0; - - while (true) { - final stop = start + chunkSize; - if (stop > length) break; - yield substring(start, stop); - start = stop; - } - - if (start < length) { - yield substring(start); - } - } -} - -extension SuperLogRecord on LogRecord { - String toPrettyString([String? extraLines]) { - final header = "[$loggerName] [$level] [$time]"; - - var msg = "$header $message"; - - if (error != null) { - msg += "\n⤷ type: ${error.runtimeType}\n⤷ error: $error"; - } - if (stackTrace != null) { - msg += "\n⤷ trace: $stackTrace"; - } - - for (var line in extraLines?.split('\n') ?? []) { - msg += '\n$header $line'; - } - - return msg; - } -} - -class LogConfig { - /// The DSN for a Sentry app. - /// This can be obtained from the Sentry apps's "settings > Client Keys (DSN)" page. - /// - /// Only logs containing errors are sent to sentry. - /// Errors can be caught using a try-catch block, like so: - /// - /// ``` - /// final logger = Logger("main"); - /// - /// try { - /// // do something dangerous here - /// } catch(e, trace) { - /// logger.info("Huston, we have a problem", e, trace); - /// } - /// ``` - /// - /// If this is [null], Sentry logger is completely disabled (default). - String? sentryDsn; - - String? tunnel; - - /// A built-in retry mechanism for sending errors to sentry. - /// - /// This parameter defines the time to wait for, before retrying. - Duration sentryRetryDelay; - - /// Path of the directory where log files will be stored. - /// - /// If this is [null], file logging is completely disabled (default). - /// - /// If this is an empty string (['']), - /// then a 'logs' directory will be created in [getTemporaryDirectory()]. - /// - /// A non-empty string will be treated as an explicit path to a directory. - /// - /// The chosen directory can be accessed using [SuperLogging.logFile.parent]. - String? logDirPath; - - /// The maximum number of log files inside [logDirPath]. - /// - /// One log file is created per day. - /// Older log files are deleted automatically. - int maxLogFiles; - - /// Whether to enable super logging features in debug mode. - /// - /// Sentry and file logging are typically not needed in debug mode, - /// where a complete logcat is available. - bool enableInDebugMode; - - /// If provided, super logging will invoke this function, and - /// any uncaught errors during its execution will be reported. - /// - /// Works by using [FlutterError.onError] and [runZoned]. - FutureOrVoidCallback? body; - - /// The date format for storing log files. - /// - /// `DateFormat('y-M-d')` by default. - DateFormat? dateFmt; - - String prefix; - - LogConfig({ - this.sentryDsn, - this.tunnel, - this.sentryRetryDelay = const Duration(seconds: 30), - this.logDirPath, - this.maxLogFiles = 10, - this.enableInDebugMode = false, - this.body, - this.dateFmt, - this.prefix = "", - }) { - dateFmt ??= DateFormat("y-M-d"); - } -} - -class SuperLogging { - /// The logger for SuperLogging - static final $ = Logger('ente_logging'); - - /// The current super logging configuration - static late LogConfig config; - - static late SharedPreferences _preferences; - - static const keyShouldReportErrors = "should_report_errors"; - - static const keyAnonymousUserID = "anonymous_user_id"; - - static Future main([LogConfig? appConfig]) async { - appConfig ??= LogConfig(); - SuperLogging.config = appConfig; - - WidgetsFlutterBinding.ensureInitialized(); - _preferences = await SharedPreferences.getInstance(); - - appVersion ??= await getAppVersion(); - - final enable = appConfig.enableInDebugMode || kReleaseMode; - sentryIsEnabled = - enable && appConfig.sentryDsn != null && shouldReportErrors(); - fileIsEnabled = enable && appConfig.logDirPath != null; - - if (fileIsEnabled) { - await setupLogDir(); - } - if (sentryIsEnabled) { - setupSentry().ignore(); - } - - Logger.root.level = Level.ALL; - Logger.root.onRecord.listen(onLogRecord); - - if (!enable) { - $.info("detected debug mode; sentry & file logging disabled."); - } - if (fileIsEnabled) { - $.info("log file for today: $logFile with prefix ${appConfig.prefix}"); - } - if (sentryIsEnabled) { - $.info("sentry uploader started"); - } - - if (appConfig.body == null) return; - - if (enable && sentryIsEnabled) { - await SentryFlutter.init( - (options) { - options.dsn = appConfig!.sentryDsn; - options.httpClient = http.Client(); - if (appConfig.tunnel != null) { - options.transport = - TunneledTransport(Uri.parse(appConfig.tunnel!), options); - } - }, - appRunner: () => appConfig!.body!(), - ); - } else { - await appConfig.body!(); - } - } - - static void setUserID(String userID) async { - if (config.sentryDsn != null) { - Sentry.configureScope((scope) => scope.setUser(SentryUser(id: userID))); - $.info("setting sentry user ID to: $userID"); - } - } - - static Future _sendErrorToSentry( - Object error, - StackTrace? stack, - ) async { - try { - await Sentry.captureException( - error, - stackTrace: stack, - ); - } catch (e) { - $.info('Sending report to sentry.io failed: $e'); - $.info('Original error: $error'); - } - } - - static String _lastExtraLines = ''; - - static Future onLogRecord(LogRecord rec) async { - // log misc info if it changed - String? extraLines = "app version: '$appVersion'\n"; - if (extraLines != _lastExtraLines) { - _lastExtraLines = extraLines; - } else { - extraLines = null; - } - - final str = "${config.prefix} ${rec.toPrettyString(extraLines)}"; - - // write to stdout - printLog(str); - - // push to log queue - if (fileIsEnabled) { - fileQueueEntries.add('$str\n'); - if (fileQueueEntries.length == 1) { - flushQueue(); - } - } - - // add error to sentry queue - if (sentryIsEnabled && rec.error != null) { - _sendErrorToSentry(rec.error!, null).ignore(); - } - } - - static final Queue fileQueueEntries = Queue(); - static bool isFlushing = false; - - static void flushQueue() async { - if (isFlushing || logFile == null) { - return; - } - isFlushing = true; - final entry = fileQueueEntries.removeFirst(); - await logFile!.writeAsString(entry, mode: FileMode.append, flush: true); - isFlushing = false; - if (fileQueueEntries.isNotEmpty) { - flushQueue(); - } - } - - // Logs on must be chunked or they get truncated otherwise - // See https://github.com/flutter/flutter/issues/22665 - static var logChunkSize = 800; - - static void printLog(String text) { - text.chunked(logChunkSize).forEach(debugPrint); - } - - /// A queue to be consumed by [setupSentry]. - static final sentryQueueControl = StreamController(); - - /// Whether sentry logging is currently enabled or not. - static bool sentryIsEnabled = false; - - static Future setupSentry() async { - $.info("Setting up sentry"); - SuperLogging.setUserID(await _getOrCreateAnonymousUserID()); - await for (final error in sentryQueueControl.stream.asBroadcastStream()) { - try { - await Sentry.captureException( - error, - ); - } catch (e) { - $.fine( - "sentry upload failed; will retry after ${config.sentryRetryDelay}", - ); - doSentryRetry(error); - } - } - } - - static void doSentryRetry(Error error) async { - await Future.delayed(config.sentryRetryDelay); - sentryQueueControl.add(error); - } - - static bool shouldReportErrors() { - if (_preferences.containsKey(keyShouldReportErrors)) { - return _preferences.getBool(keyShouldReportErrors)!; - } else { - return kDebugMode; - } - } - - static Future setShouldReportErrors(bool value) { - return _preferences.setBool(keyShouldReportErrors, value); - } - - static Future _getOrCreateAnonymousUserID() async { - if (!_preferences.containsKey(keyAnonymousUserID)) { - //ignore: prefer_const_constructors - await _preferences.setString(keyAnonymousUserID, Uuid().v4()); - } - return _preferences.getString(keyAnonymousUserID)!; - } - - /// The log file currently in use. - static File? logFile; - - /// Whether file logging is currently enabled or not. - static bool fileIsEnabled = false; - - static Future setupLogDir() async { - var dirPath = config.logDirPath; - - // choose [logDir] - if (dirPath == null || dirPath.isEmpty) { - final root = await getExternalStorageDirectory(); - dirPath = '${root!.path}/logs'; - } - - // create [logDir] - final dir = Directory(dirPath); - await dir.create(recursive: true); - - final files = []; - final dates = {}; - - // collect all log files with valid names - await for (final file in dir.list()) { - try { - final date = config.dateFmt!.parse(basename(file.path)); - dates[file as File] = date; - files.add(file); - } on Exception catch (_) {} - } - final nowTime = DateTime.now(); - - // delete old log files, if [maxLogFiles] is exceeded. - if (files.length > config.maxLogFiles) { - // sort files based on ascending order of date (older first) - files.sort( - (a, b) => (dates[a] ?? nowTime).compareTo((dates[b] ?? nowTime)), - ); - - final extra = files.length - config.maxLogFiles; - final toDelete = files.sublist(0, extra); - - for (final file in toDelete) { - try { - $.fine( - "deleting log file ${file.path}", - ); - await file.delete(); - } on Exception catch (_) {} - } - } - - logFile = File("$dirPath/${config.dateFmt!.format(DateTime.now())}.txt"); - } - - /// Current app version, obtained from package_info plugin. - /// - /// See: [getAppVersion] - static String? appVersion; - - static Future getAppVersion() async { - final pkgInfo = await PackageInfo.fromPlatform(); - return "${pkgInfo.version}+${pkgInfo.buildNumber}"; - } - - // disable sentry on f-droid. We need to make it opt-in preference - static Future isFDroidBuild() async { - if (!Platform.isAndroid) { - return false; - } - final pkgName = (await PackageInfo.fromPlatform()).packageName; - return pkgName.endsWith("fdroid"); - } -} diff --git a/mobile/apps/auth/lib/core/logging/tunneled_transport.dart b/mobile/apps/auth/lib/core/logging/tunneled_transport.dart deleted file mode 100644 index f9ffd5ad12..0000000000 --- a/mobile/apps/auth/lib/core/logging/tunneled_transport.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'dart:convert'; - -import 'package:http/http.dart'; -import 'package:sentry/sentry.dart'; - -/// A transport is in charge of sending the event to the Sentry server. -class TunneledTransport implements Transport { - final Uri _tunnel; - final SentryOptions _options; - - final Dsn? _dsn; - - _CredentialBuilder? _credentialBuilder; - - final Map _headers; - - factory TunneledTransport(Uri tunnel, SentryOptions options) { - return TunneledTransport._(tunnel, options); - } - - TunneledTransport._(this._tunnel, this._options) - : _dsn = _options.dsn != null ? Dsn.parse(_options.dsn!) : null, - _headers = _buildHeaders( - _options.platformChecker.isWeb, - _options.sentryClientName, - ) { - _credentialBuilder = _CredentialBuilder( - _dsn, - _options.sentryClientName, - // ignore: invalid_use_of_internal_member - _options.clock, - ); - } - - @override - Future send(SentryEnvelope envelope) async { - final streamedRequest = await _createStreamedRequest(envelope); - final response = await _options.httpClient - .send(streamedRequest) - .then(Response.fromStream); - - if (response.statusCode != 200) { - // body guard to not log the error as it has performance impact to allocate - // the body String. - if (_options.debug) { - _options.logger( - SentryLevel.error, - 'API returned an error, statusCode = ${response.statusCode}, ' - 'body = ${response.body}', - ); - } - return const SentryId.empty(); - } else { - _options.logger( - SentryLevel.debug, - 'Envelope ${envelope.header.eventId ?? "--"} was sent successfully.', - ); - } - - final eventId = json.decode(response.body)['id']; - if (eventId == null) { - return null; - } - return SentryId.fromId(eventId); - } - - Future _createStreamedRequest( - SentryEnvelope envelope, - ) async { - final streamedRequest = StreamedRequest('POST', _tunnel); - envelope - .envelopeStream(_options) - .listen(streamedRequest.sink.add) - .onDone(streamedRequest.sink.close); - - streamedRequest.headers.addAll(_credentialBuilder!.configure(_headers)); - - return streamedRequest; - } -} - -class _CredentialBuilder { - final String _authHeader; - - final ClockProvider _clock; - - int get timestamp => _clock().millisecondsSinceEpoch; - - _CredentialBuilder._(String authHeader, ClockProvider clock) - : _authHeader = authHeader, - _clock = clock; - - factory _CredentialBuilder( - Dsn? dsn, - String sdkIdentifier, - ClockProvider clock, - ) { - final authHeader = _buildAuthHeader( - publicKey: dsn?.publicKey, - secretKey: dsn?.secretKey, - sdkIdentifier: sdkIdentifier, - ); - - return _CredentialBuilder._(authHeader, clock); - } - - static String _buildAuthHeader({ - String? publicKey, - String? secretKey, - String? sdkIdentifier, - }) { - var header = 'Sentry sentry_version=7, sentry_client=$sdkIdentifier, ' - 'sentry_key=$publicKey'; - - if (secretKey != null) { - header += ', sentry_secret=$secretKey'; - } - - return header; - } - - Map configure(Map headers) { - return headers - ..addAll( - { - 'X-Sentry-Auth': '$_authHeader, sentry_timestamp=$timestamp', - }, - ); - } -} - -Map _buildHeaders(bool isWeb, String sdkIdentifier) { - final headers = {'Content-Type': 'application/x-sentry-envelope'}; - // NOTE(lejard_h) overriding user agent on VM and Flutter not sure why - // for web it use browser user agent - if (!isWeb) { - headers['User-Agent'] = sdkIdentifier; - } - return headers; -} diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 54f8da92aa..5da7d0a6f9 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -5,7 +5,6 @@ import 'package:adaptive_theme/adaptive_theme.dart'; import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/logging/super_logging.dart'; import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/locale.dart'; @@ -26,10 +25,10 @@ import 'package:ente_auth/utils/lock_screen_settings.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/window_protocol_handler.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_logging/logging.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_displaymode/flutter_displaymode.dart'; -import 'package:logging/logging.dart'; import 'package:path_provider/path_provider.dart'; import 'package:tray_manager/tray_manager.dart'; import 'package:window_manager/window_manager.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/general_section_widget.dart b/mobile/apps/auth/lib/ui/settings/general_section_widget.dart index cb29d8483a..aadb974b24 100644 --- a/mobile/apps/auth/lib/ui/settings/general_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/general_section_widget.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:ente_auth/app/view/app.dart'; import 'package:ente_auth/core/event_bus.dart'; -import 'package:ente_auth/core/logging/super_logging.dart'; import 'package:ente_auth/events/icons_changed_event.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/locale.dart'; @@ -16,6 +15,7 @@ import 'package:ente_auth/ui/settings/common_settings.dart'; import 'package:ente_auth/ui/settings/language_picker.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; +import 'package:ente_logging/logging.dart'; import 'package:flutter/material.dart'; class AdvancedSectionWidget extends StatefulWidget { diff --git a/mobile/apps/auth/lib/utils/email_util.dart b/mobile/apps/auth/lib/utils/email_util.dart index 3c35769574..c58553c692 100644 --- a/mobile/apps/auth/lib/utils/email_util.dart +++ b/mobile/apps/auth/lib/utils/email_util.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:archive/archive_io.dart'; import 'package:email_validator/email_validator.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/logging/super_logging.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/dialog_widget.dart'; @@ -14,11 +13,11 @@ import 'package:ente_auth/utils/directory_utils.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/share_utils.dart'; import 'package:ente_auth/utils/toast_util.dart'; +import 'package:ente_logging/logging.dart'; import "package:file_saver/file_saver.dart"; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import "package:intl/intl.dart"; -import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 4b9e12285f..c8725a89b1 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -407,6 +407,13 @@ packages: url: "https://github.com/ente-io/ente_crypto_dart.git" source: git version: "1.0.0" + ente_logging: + dependency: "direct main" + description: + path: "../../packages/logging" + relative: true + source: path + version: "1.0.0" event_bus: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index ea666a5317..ed015d040b 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -28,6 +28,8 @@ dependencies: ente_crypto_dart: git: url: https://github.com/ente-io/ente_crypto_dart.git + ente_logging: + path: ../../packages/logging event_bus: ^2.0.0 expandable: ^5.0.1 expansion_tile_card: ^3.0.0 From eb3e3db8e6a23dcc895c9daa533e1b6a4bfd0b44 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 20:43:35 +0530 Subject: [PATCH 010/164] Base config --- .../lib/{configuration.dart => base_configuration.dart} | 9 +++++---- mobile/packages/configuration/pubspec.yaml | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) rename mobile/packages/configuration/lib/{configuration.dart => base_configuration.dart} (98%) diff --git a/mobile/packages/configuration/lib/configuration.dart b/mobile/packages/configuration/lib/base_configuration.dart similarity index 98% rename from mobile/packages/configuration/lib/configuration.dart rename to mobile/packages/configuration/lib/base_configuration.dart index bfe8c5ef43..75acb8e687 100644 --- a/mobile/packages/configuration/lib/configuration.dart +++ b/mobile/packages/configuration/lib/base_configuration.dart @@ -5,7 +5,7 @@ import 'dart:io' as io; import 'dart:typed_data'; import 'package:bip39/bip39.dart' as bip39; -import 'package:configuration/constants.dart'; +import 'package:ente_configuration/constants.dart'; import 'package:ente_base/models/database.dart'; import 'package:ente_base/models/key_attributes.dart'; import 'package:ente_base/models/key_gen_result.dart'; @@ -21,10 +21,11 @@ import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; -class Configuration { - Configuration._privateConstructor(); +class BaseConfiguration { + BaseConfiguration._privateConstructor(); - static final Configuration instance = Configuration._privateConstructor(); + static final BaseConfiguration instance = + BaseConfiguration._privateConstructor(); static const endpoint = String.fromEnvironment( "endpoint", defaultValue: kDefaultProductionEndpoint, diff --git a/mobile/packages/configuration/pubspec.yaml b/mobile/packages/configuration/pubspec.yaml index df08596507..919439c9cc 100644 --- a/mobile/packages/configuration/pubspec.yaml +++ b/mobile/packages/configuration/pubspec.yaml @@ -1,4 +1,4 @@ -name: configuration +name: ente_configuration description: A Flutter package for shared configuration across ente apps version: 1.0.0 publish_to: none From 134314c2853431caa8129ae343a182013b09716c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 12 Jul 2025 21:27:47 +0530 Subject: [PATCH 011/164] Common config --- mobile/apps/auth/docs/vscode/launch.json | 10 +- mobile/apps/auth/ios/Podfile.lock | 77 ++-- mobile/apps/auth/lib/app/view/app.dart | 6 +- mobile/apps/auth/lib/core/configuration.dart | 408 +----------------- mobile/apps/auth/lib/core/event_bus.dart | 5 - mobile/apps/auth/lib/core/network.dart | 4 +- .../auth/lib/events/codes_updated_event.dart | 2 +- .../lib/events/endpoint_updated_event.dart | 3 - mobile/apps/auth/lib/events/event.dart | 3 - .../auth/lib/events/icons_changed_event.dart | 2 +- .../auth/lib/events/notification_event.dart | 5 - .../apps/auth/lib/events/signed_in_event.dart | 3 - .../auth/lib/events/signed_out_event.dart | 4 +- .../auth/lib/events/trigger_logout_event.dart | 2 +- .../events/user_details_changed_event.dart | 3 - mobile/apps/auth/lib/main.dart | 3 +- .../apps/auth/lib/models/key_attributes.dart | 102 ----- .../apps/auth/lib/models/key_gen_result.dart | 12 - .../lib/models/private_key_attributes.dart | 7 - .../lib/onboarding/view/onboarding_page.dart | 2 +- .../view/setup_enter_secret_key_page.dart | 2 +- .../lib/services/authenticator_service.dart | 4 +- .../auth/lib/services/preference_service.dart | 2 +- .../apps/auth/lib/services/user_service.dart | 8 +- .../apps/auth/lib/store/authenticator_db.dart | 3 +- mobile/apps/auth/lib/store/code_store.dart | 2 +- .../lib/ui/account/password_entry_page.dart | 3 +- .../auth/lib/ui/home/coach_mark_widget.dart | 2 +- mobile/apps/auth/lib/ui/home_page.dart | 2 +- .../ui/settings/general_section_widget.dart | 2 +- .../auth/lib/utils/lock_screen_settings.dart | 4 +- mobile/apps/auth/pubspec.lock | 21 + mobile/apps/auth/pubspec.yaml | 6 + .../configuration/lib/base_configuration.dart | 91 ++-- 34 files changed, 169 insertions(+), 646 deletions(-) delete mode 100644 mobile/apps/auth/lib/core/event_bus.dart delete mode 100644 mobile/apps/auth/lib/events/endpoint_updated_event.dart delete mode 100644 mobile/apps/auth/lib/events/event.dart delete mode 100644 mobile/apps/auth/lib/events/notification_event.dart delete mode 100644 mobile/apps/auth/lib/events/signed_in_event.dart delete mode 100644 mobile/apps/auth/lib/events/user_details_changed_event.dart delete mode 100644 mobile/apps/auth/lib/models/key_attributes.dart delete mode 100644 mobile/apps/auth/lib/models/key_gen_result.dart delete mode 100644 mobile/apps/auth/lib/models/private_key_attributes.dart diff --git a/mobile/apps/auth/docs/vscode/launch.json b/mobile/apps/auth/docs/vscode/launch.json index cf6e037e34..1430e5bd9d 100644 --- a/mobile/apps/auth/docs/vscode/launch.json +++ b/mobile/apps/auth/docs/vscode/launch.json @@ -6,14 +6,14 @@ "request": "launch", "type": "dart", "flutterMode": "debug", - "program": "auth/lib/main.dart", + "program": "mobile/apps/auth/lib/main.dart", "args": ["--dart-define", "endpoint=http://localhost:8080"] }, { "name": "Auth Android Dev", "request": "launch", "type": "dart", - "program": "auth/lib/main.dart", + "program": "mobile/apps/auth/lib/main.dart", "args": [ "--dart-define", "endpoint=http://192.168.1.3:8080", @@ -25,21 +25,21 @@ "name": "Auth iOS Dev", "request": "launch", "type": "dart", - "program": "auth/lib/main.dart", + "program": "mobile/apps/auth/lib/main.dart", "args": ["--dart-define", "endpoint=http://192.168.1.30:8080"] }, { "name": "Auth iOS Prod", "request": "launch", "type": "dart", - "program": "auth/lib/main.dart", + "program": "mobile/apps/auth/lib/main.dart", "args": ["--target", "lib/main.dart"] }, { "name": "Auth Android Prod", "request": "launch", "type": "dart", - "program": "auth/lib/main.dart", + "program": "mobile/apps/auth/lib/main.dart", "args": ["--target", "lib/main.dart", "--flavor", "independent"] } ] diff --git a/mobile/apps/auth/ios/Podfile.lock b/mobile/apps/auth/ios/Podfile.lock index 6a2db70ea9..688ae6f430 100644 --- a/mobile/apps/auth/ios/Podfile.lock +++ b/mobile/apps/auth/ios/Podfile.lock @@ -3,7 +3,6 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter - - FlutterMacOS - cupertino_http (0.0.1): - Flutter - FlutterMacOS @@ -61,13 +60,12 @@ PODS: - Flutter - flutter_local_notifications (0.0.1): - Flutter - - flutter_native_splash (0.0.1): + - flutter_native_splash (2.4.3): - Flutter - flutter_secure_storage (6.0.0): - Flutter - fluttertoast (0.0.2): - Flutter - - Toast - local_auth_darwin (0.0.1): - Flutter - FlutterMacOS @@ -87,9 +85,9 @@ PODS: - qr_code_scanner (0.2.0): - Flutter - MTBBarcodeScanner - - SDWebImage (5.21.0): - - SDWebImage/Core (= 5.21.0) - - SDWebImage/Core (5.21.0) + - SDWebImage (5.21.1): + - SDWebImage/Core (= 5.21.1) + - SDWebImage/Core (5.21.1) - Sentry/HybridSDK (8.46.0) - sentry_flutter (8.14.2): - Flutter @@ -102,35 +100,38 @@ PODS: - FlutterMacOS - sodium_libs (2.2.1): - Flutter - - sqflite (0.0.3): + - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - "sqlite3 (3.46.1+1)": - - "sqlite3/common (= 3.46.1+1)" - - "sqlite3/common (3.46.1+1)" - - "sqlite3/dbstatvtab (3.46.1+1)": + - sqlite3 (3.50.2): + - sqlite3/common (= 3.50.2) + - sqlite3/common (3.50.2) + - sqlite3/dbstatvtab (3.50.2): - sqlite3/common - - "sqlite3/fts5 (3.46.1+1)": + - sqlite3/fts5 (3.50.2): - sqlite3/common - - "sqlite3/perf-threadsafe (3.46.1+1)": + - sqlite3/math (3.50.2): - sqlite3/common - - "sqlite3/rtree (3.46.1+1)": + - sqlite3/perf-threadsafe (3.50.2): + - sqlite3/common + - sqlite3/rtree (3.50.2): - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter - - "sqlite3 (~> 3.46.0+1)" + - FlutterMacOS + - sqlite3 (~> 3.50.1) - sqlite3/dbstatvtab - sqlite3/fts5 + - sqlite3/math - sqlite3/perf-threadsafe - sqlite3/rtree - SwiftyGif (5.4.5) - - Toast (4.1.1) - url_launcher_ios (0.0.1): - Flutter DEPENDENCIES: - app_links (from `.symlinks/plugins/app_links/ios`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - file_picker (from `.symlinks/plugins/file_picker/ios`) @@ -155,8 +156,8 @@ DEPENDENCIES: - share_plus (from `.symlinks/plugins/share_plus/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sodium_libs (from `.symlinks/plugins/sodium_libs/ios`) - - sqflite (from `.symlinks/plugins/sqflite/darwin`) - - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + - sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/darwin`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) SPEC REPOS: @@ -169,13 +170,12 @@ SPEC REPOS: - Sentry - sqlite3 - SwiftyGif - - Toast EXTERNAL SOURCES: app_links: :path: ".symlinks/plugins/app_links/ios" connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/darwin" + :path: ".symlinks/plugins/connectivity_plus/ios" cupertino_http: :path: ".symlinks/plugins/cupertino_http/darwin" device_info_plus: @@ -224,51 +224,50 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sodium_libs: :path: ".symlinks/plugins/sodium_libs/ios" - sqflite: - :path: ".symlinks/plugins/sqflite/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" sqlite3_flutter_libs: - :path: ".symlinks/plugins/sqlite3_flutter_libs/ios" + :path: ".symlinks/plugins/sqlite3_flutter_libs/darwin" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" SPEC CHECKSUMS: - app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0 - connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db + app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6 + connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 - file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808 fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40 + flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb - flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086 - flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778 + flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 + flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 - fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c - local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 + fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f + local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb objective_c: 77e887b5ba1827970907e10e832eec1683f3431d OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 - package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e - SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 + SDWebImage: f29024626962457f3470184232766516dee8dfea Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b - share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad + share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318 - sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb - sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + sqlite3: 3e82a2daae39ba3b41ae6ee84a130494585460fc + sqlite3_flutter_libs: 2c48c4ee7217fd653251975e43412250d5bcbbe2 SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 78f002751f1a8f65042b8da97902ba4124271c5a diff --git a/mobile/apps/auth/lib/app/view/app.dart b/mobile/apps/auth/lib/app/view/app.dart index b6ff65001b..aee7b610b5 100644 --- a/mobile/apps/auth/lib/app/view/app.dart +++ b/mobile/apps/auth/lib/app/view/app.dart @@ -3,10 +3,7 @@ import 'dart:io'; import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/events/signed_in_event.dart'; -import 'package:ente_auth/events/signed_out_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/locale.dart'; import "package:ente_auth/onboarding/view/onboarding_page.dart"; @@ -16,6 +13,9 @@ import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/services/window_listener_service.dart'; import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/ui/settings/app_update_dialog.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/signed_in_event.dart'; +import 'package:ente_events/models/signed_out_event.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_localizations/flutter_localizations.dart'; diff --git a/mobile/apps/auth/lib/core/configuration.dart b/mobile/apps/auth/lib/core/configuration.dart index 7ba6fa4922..79299ed3c6 100644 --- a/mobile/apps/auth/lib/core/configuration.dart +++ b/mobile/apps/auth/lib/core/configuration.dart @@ -1,95 +1,36 @@ import 'dart:async'; -import 'dart:convert'; -import 'dart:io' as io; import 'dart:typed_data'; -import 'package:bip39/bip39.dart' as bip39; -import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/event_bus.dart'; -import 'package:ente_auth/events/endpoint_updated_event.dart'; -import 'package:ente_auth/events/signed_in_event.dart'; -import 'package:ente_auth/events/signed_out_event.dart'; -import 'package:ente_auth/models/key_attributes.dart'; -import 'package:ente_auth/models/key_gen_result.dart'; -import 'package:ente_auth/models/private_key_attributes.dart'; -import 'package:ente_auth/store/authenticator_db.dart'; -import 'package:ente_auth/utils/directory_utils.dart'; +import 'package:ente_base/models/database.dart'; +import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; -import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; -import 'package:tuple/tuple.dart'; -class Configuration { +class Configuration extends BaseConfiguration { Configuration._privateConstructor(); static final Configuration instance = Configuration._privateConstructor(); - static const endpoint = String.fromEnvironment( - "endpoint", - defaultValue: kDefaultProductionEndpoint, - ); - static const emailKey = "email"; - static const keyAttributesKey = "key_attributes"; - - static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time"; - static const keyKey = "key"; - static const secretKeyKey = "secret_key"; static const authSecretKeyKey = "auth_secret_key"; static const offlineAuthSecretKey = "offline_auth_secret_key"; - static const tokenKey = "token"; - static const encryptedTokenKey = "encrypted_token"; - static const userIDKey = "user_id"; - static const hasMigratedSecureStorageKey = "has_migrated_secure_storage"; static const hasOptedForOfflineModeKey = "has_opted_for_offline_mode"; - static const endPointKey = "endpoint"; - final List onlineSecureKeys = [ - keyKey, - secretKeyKey, - authSecretKeyKey, - ]; - final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds; - - static final _logger = Logger("Configuration"); - - String? _cachedToken; late SharedPreferences _preferences; - String? _key; - String? _secretKey; String? _authSecretKey; String? _offlineAuthKey; late FlutterSecureStorage _secureStorage; - late String _tempDirectory; - String? _volatilePassword; - - Future init() async { + @override + Future init(List dbs) async { + await super.init(dbs); _preferences = await SharedPreferences.getInstance(); - sqfliteFfiInit(); _secureStorage = const FlutterSecureStorage( iOptions: IOSOptions( accessibility: KeychainAccessibility.first_unlock_this_device, ), ); - _tempDirectory = (await DirectoryUtils.getDirectoryForInit()).path; - final tempDirectory = io.Directory(_tempDirectory); - try { - final currentTime = DateTime.now().microsecondsSinceEpoch; - if (tempDirectory.existsSync() && - (_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) < - (currentTime - kTempFolderDeletionTimeBuffer)) { - await tempDirectory.delete(recursive: true); - await _preferences.setInt(lastTempFolderClearTimeKey, currentTime); - _logger.info("Cleared temp folder"); - } else { - _logger.info("Skipping temp folder clear"); - } - } catch (e) { - _logger.warning(e); - } - tempDirectory.createSync(recursive: true); - await _initOnlineAccount(); + sqfliteFfiInit(); await _initOfflineAccount(); } @@ -99,303 +40,10 @@ class Configuration { ); } - Future _initOnlineAccount() async { - if (!_preferences.containsKey(tokenKey)) { - for (final key in onlineSecureKeys) { - unawaited( - _secureStorage.delete( - key: key, - ), - ); - } - } else { - _key = await _secureStorage.read( - key: keyKey, - ); - _secretKey = await _secureStorage.read( - key: secretKeyKey, - ); - _authSecretKey = await _secureStorage.read( - key: authSecretKeyKey, - ); - if (_key == null) { - await logout(autoLogout: true); - } - } - } - + @override Future logout({bool autoLogout = false}) async { - await _preferences.clear(); - for (String key in onlineSecureKeys) { - await _secureStorage.delete( - key: key, - ); - } - await AuthenticatorDB.instance.clearTable(); - _key = null; - _cachedToken = null; - _secretKey = null; _authSecretKey = null; - Bus.instance.fire(SignedOutEvent()); - } - - Future generateKey(String password) async { - // Create a master key - final masterKey = CryptoUtil.generateKey(); - - // Create a recovery key - final recoveryKey = CryptoUtil.generateKey(); - - // Encrypt master key and recovery key with each other - final encryptedMasterKey = CryptoUtil.encryptSync(masterKey, recoveryKey); - final encryptedRecoveryKey = CryptoUtil.encryptSync(recoveryKey, masterKey); - - // Derive a key from the password that will be used to encrypt and - // decrypt the master key - final kekSalt = CryptoUtil.getSaltToDeriveKey(); - final derivedKeyResult = await CryptoUtil.deriveSensitiveKey( - utf8.encode(password), - kekSalt, - ); - final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key); - - // Encrypt the key with this derived key - final encryptedKeyData = - CryptoUtil.encryptSync(masterKey, derivedKeyResult.key); - - // Generate a public-private keypair and encrypt the latter - final keyPair = CryptoUtil.generateKeyPair(); - final encryptedSecretKeyData = - CryptoUtil.encryptSync(keyPair.secretKey.extractBytes(), masterKey); - - final attributes = KeyAttributes( - CryptoUtil.bin2base64(kekSalt), - CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), - CryptoUtil.bin2base64(encryptedKeyData.nonce!), - CryptoUtil.bin2base64(keyPair.publicKey), - CryptoUtil.bin2base64(encryptedSecretKeyData.encryptedData!), - CryptoUtil.bin2base64(encryptedSecretKeyData.nonce!), - derivedKeyResult.memLimit, - derivedKeyResult.opsLimit, - CryptoUtil.bin2base64(encryptedMasterKey.encryptedData!), - CryptoUtil.bin2base64(encryptedMasterKey.nonce!), - CryptoUtil.bin2base64(encryptedRecoveryKey.encryptedData!), - CryptoUtil.bin2base64(encryptedRecoveryKey.nonce!), - ); - final privateAttributes = PrivateKeyAttributes( - CryptoUtil.bin2base64(masterKey), - CryptoUtil.bin2hex(recoveryKey), - CryptoUtil.bin2base64(keyPair.secretKey.extractBytes()), - ); - return KeyGenResult(attributes, privateAttributes, loginKey); - } - - Future> getAttributesForNewPassword( - String password, - ) async { - // Get master key - final masterKey = getKey(); - - // Derive a key from the password that will be used to encrypt and - // decrypt the master key - final kekSalt = CryptoUtil.getSaltToDeriveKey(); - final derivedKeyResult = await CryptoUtil.deriveSensitiveKey( - utf8.encode(password), - kekSalt, - ); - final loginKey = await CryptoUtil.deriveLoginKey(derivedKeyResult.key); - - // Encrypt the key with this derived key - final encryptedKeyData = - CryptoUtil.encryptSync(masterKey!, derivedKeyResult.key); - - final existingAttributes = getKeyAttributes(); - - final updatedAttributes = existingAttributes!.copyWith( - kekSalt: CryptoUtil.bin2base64(kekSalt), - encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), - keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!), - memLimit: derivedKeyResult.memLimit, - opsLimit: derivedKeyResult.opsLimit, - ); - return Tuple2(updatedAttributes, loginKey); - } - - // decryptSecretsAndGetLoginKey decrypts the master key and recovery key - // with the given password and save them in local secure storage. - // This method also returns the keyEncKey that can be used for performing - // SRP setup for existing users. - Future decryptSecretsAndGetKeyEncKey( - String password, - KeyAttributes attributes, { - Uint8List? keyEncryptionKey, - }) async { - _logger.info('Start decryptAndSaveSecrets'); - keyEncryptionKey ??= await CryptoUtil.deriveKey( - utf8.encode(password), - CryptoUtil.base642bin(attributes.kekSalt), - attributes.memLimit, - attributes.opsLimit, - ); - - _logger.info('user-key done'); - Uint8List key; - try { - key = CryptoUtil.decryptSync( - CryptoUtil.base642bin(attributes.encryptedKey), - keyEncryptionKey, - CryptoUtil.base642bin(attributes.keyDecryptionNonce), - ); - } catch (e) { - _logger.severe('master-key failed, incorrect password?', e); - throw Exception("Incorrect password"); - } - _logger.info("master-key done"); - await setKey(CryptoUtil.bin2base64(key)); - final secretKey = CryptoUtil.decryptSync( - CryptoUtil.base642bin(attributes.encryptedSecretKey), - key, - CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce), - ); - _logger.info("secret-key done"); - await setSecretKey(CryptoUtil.bin2base64(secretKey)); - final token = CryptoUtil.openSealSync( - CryptoUtil.base642bin(getEncryptedToken()!), - CryptoUtil.base642bin(attributes.publicKey), - secretKey, - ); - _logger.info('appToken done'); - await setToken( - CryptoUtil.bin2base64(token, urlSafe: true), - ); - return keyEncryptionKey; - } - - Future recover(String recoveryKey) async { - // check if user has entered mnemonic code - if (recoveryKey.contains(' ')) { - final split = recoveryKey.split(' '); - if (split.length != mnemonicKeyWordCount) { - String wordThatIsFollowedByEmptySpaceInSplit = ''; - for (int i = 0; i < split.length; i++) { - String word = split[i]; - if (word.isEmpty) { - wordThatIsFollowedByEmptySpaceInSplit = - '\n\nExtra space after word at position $i'; - break; - } - } - throw AssertionError( - '\nRecovery code should have $mnemonicKeyWordCount words, ' - 'found ${split.length} words instead.$wordThatIsFollowedByEmptySpaceInSplit', - ); - } - recoveryKey = bip39.mnemonicToEntropy(recoveryKey); - } - final attributes = getKeyAttributes(); - Uint8List masterKey; - try { - masterKey = await CryptoUtil.decrypt( - CryptoUtil.base642bin(attributes!.masterKeyEncryptedWithRecoveryKey), - CryptoUtil.hex2bin(recoveryKey), - CryptoUtil.base642bin(attributes.masterKeyDecryptionNonce), - ); - } catch (e) { - _logger.severe(e); - rethrow; - } - await setKey(CryptoUtil.bin2base64(masterKey)); - final secretKey = CryptoUtil.decryptSync( - CryptoUtil.base642bin(attributes.encryptedSecretKey), - masterKey, - CryptoUtil.base642bin(attributes.secretKeyDecryptionNonce), - ); - await setSecretKey(CryptoUtil.bin2base64(secretKey)); - final token = CryptoUtil.openSealSync( - CryptoUtil.base642bin(getEncryptedToken()!), - CryptoUtil.base642bin(attributes.publicKey), - secretKey, - ); - await setToken( - CryptoUtil.bin2base64(token, urlSafe: true), - ); - } - - String getHttpEndpoint() { - return _preferences.getString(endPointKey) ?? endpoint; - } - - Future setHttpEndpoint(String endpoint) async { - await _preferences.setString(endPointKey, endpoint); - Bus.instance.fire(EndpointUpdatedEvent()); - } - - String? getToken() { - _cachedToken ??= _preferences.getString(tokenKey); - return _cachedToken; - } - - bool isLoggedIn() { - return getToken() != null; - } - - Future setToken(String token) async { - _cachedToken = token; - await _preferences.setString(tokenKey, token); - Bus.instance.fire(SignedInEvent()); - } - - Future setEncryptedToken(String encryptedToken) async { - await _preferences.setString(encryptedTokenKey, encryptedToken); - } - - String? getEncryptedToken() { - return _preferences.getString(encryptedTokenKey); - } - - String? getEmail() { - return _preferences.getString(emailKey); - } - - Future setEmail(String email) async { - await _preferences.setString(emailKey, email); - } - - int? getUserID() { - return _preferences.getInt(userIDKey); - } - - Future setUserID(int userID) async { - await _preferences.setInt(userIDKey, userID); - } - - Future setKeyAttributes(KeyAttributes attributes) async { - await _preferences.setString(keyAttributesKey, attributes.toJson()); - } - - KeyAttributes? getKeyAttributes() { - final jsonValue = _preferences.getString(keyAttributesKey); - if (jsonValue == null) { - return null; - } else { - return KeyAttributes.fromJson(jsonValue); - } - } - - Future setKey(String key) async { - _key = key; - await _secureStorage.write( - key: keyKey, - value: key, - ); - } - - Future setSecretKey(String? secretKey) async { - _secretKey = secretKey; - await _secureStorage.write( - key: secretKeyKey, - value: secretKey, - ); + await super.logout(); } Future setAuthSecretKey(String? authSecretKey) async { @@ -406,14 +54,6 @@ class Configuration { ); } - Uint8List? getKey() { - return _key == null ? null : CryptoUtil.base642bin(_key!); - } - - Uint8List? getSecretKey() { - return _secretKey == null ? null : CryptoUtil.base642bin(_secretKey!); - } - Uint8List? getAuthSecretKey() { return _authSecretKey == null ? null @@ -426,24 +66,6 @@ class Configuration { : CryptoUtil.base642bin(_offlineAuthKey!); } - Uint8List getRecoveryKey() { - final keyAttributes = getKeyAttributes()!; - return CryptoUtil.decryptSync( - CryptoUtil.base642bin(keyAttributes.recoveryKeyEncryptedWithMasterKey), - getKey()!, - CryptoUtil.base642bin(keyAttributes.recoveryKeyDecryptionNonce), - ); - } - - // Caution: This directory is cleared on app start - String getTempDirectory() { - return _tempDirectory; - } - - bool hasConfiguredAccount() { - return getToken() != null && _key != null; - } - bool hasOptedForOfflineMode() { return _preferences.getBool(hasOptedForOfflineModeKey) ?? false; } @@ -464,16 +86,4 @@ class Configuration { } await _preferences.setBool(hasOptedForOfflineModeKey, true); } - - void setVolatilePassword(String volatilePassword) { - _volatilePassword = volatilePassword; - } - - void resetVolatilePassword() { - _volatilePassword = null; - } - - String? getVolatilePassword() { - return _volatilePassword; - } } diff --git a/mobile/apps/auth/lib/core/event_bus.dart b/mobile/apps/auth/lib/core/event_bus.dart deleted file mode 100644 index 162a107439..0000000000 --- a/mobile/apps/auth/lib/core/event_bus.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:event_bus/event_bus.dart'; - -class Bus { - static final EventBus instance = EventBus(); -} diff --git a/mobile/apps/auth/lib/core/network.dart b/mobile/apps/auth/lib/core/network.dart index 0f5ae3555d..9e884fa159 100644 --- a/mobile/apps/auth/lib/core/network.dart +++ b/mobile/apps/auth/lib/core/network.dart @@ -2,10 +2,10 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/event_bus.dart'; -import 'package:ente_auth/events/endpoint_updated_event.dart'; import 'package:ente_auth/utils/package_info_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/endpoint_updated_event.dart'; import 'package:fk_user_agent/fk_user_agent.dart'; import 'package:flutter/foundation.dart'; import 'package:native_dio_adapter/native_dio_adapter.dart'; diff --git a/mobile/apps/auth/lib/events/codes_updated_event.dart b/mobile/apps/auth/lib/events/codes_updated_event.dart index b678413e8d..2966735f73 100644 --- a/mobile/apps/auth/lib/events/codes_updated_event.dart +++ b/mobile/apps/auth/lib/events/codes_updated_event.dart @@ -1,3 +1,3 @@ -import 'package:ente_auth/events/event.dart'; +import 'package:ente_events/models/event.dart'; class CodesUpdatedEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/endpoint_updated_event.dart b/mobile/apps/auth/lib/events/endpoint_updated_event.dart deleted file mode 100644 index 0a9915479a..0000000000 --- a/mobile/apps/auth/lib/events/endpoint_updated_event.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:ente_auth/events/event.dart'; - -class EndpointUpdatedEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/event.dart b/mobile/apps/auth/lib/events/event.dart deleted file mode 100644 index bd21892935..0000000000 --- a/mobile/apps/auth/lib/events/event.dart +++ /dev/null @@ -1,3 +0,0 @@ - - -class Event {} diff --git a/mobile/apps/auth/lib/events/icons_changed_event.dart b/mobile/apps/auth/lib/events/icons_changed_event.dart index 5cc3d8b419..545ce0a49d 100644 --- a/mobile/apps/auth/lib/events/icons_changed_event.dart +++ b/mobile/apps/auth/lib/events/icons_changed_event.dart @@ -1,3 +1,3 @@ -import 'package:ente_auth/events/event.dart'; +import 'package:ente_events/models/event.dart'; class IconsChangedEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/notification_event.dart b/mobile/apps/auth/lib/events/notification_event.dart deleted file mode 100644 index 87fe1d9060..0000000000 --- a/mobile/apps/auth/lib/events/notification_event.dart +++ /dev/null @@ -1,5 +0,0 @@ -import 'package:ente_auth/events/event.dart'; - -// NotificationEvent event is used to re-fresh the UI to show latest notification -// (if any) -class NotificationEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/signed_in_event.dart b/mobile/apps/auth/lib/events/signed_in_event.dart deleted file mode 100644 index f0720fac9a..0000000000 --- a/mobile/apps/auth/lib/events/signed_in_event.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:ente_auth/events/event.dart'; - -class SignedInEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/signed_out_event.dart b/mobile/apps/auth/lib/events/signed_out_event.dart index abe4927706..613744d49d 100644 --- a/mobile/apps/auth/lib/events/signed_out_event.dart +++ b/mobile/apps/auth/lib/events/signed_out_event.dart @@ -1,3 +1 @@ -import 'package:ente_auth/events/event.dart'; - -class SignedOutEvent extends Event {} +// TODO Implement this library. diff --git a/mobile/apps/auth/lib/events/trigger_logout_event.dart b/mobile/apps/auth/lib/events/trigger_logout_event.dart index bfdc79e940..a6c9b724f5 100644 --- a/mobile/apps/auth/lib/events/trigger_logout_event.dart +++ b/mobile/apps/auth/lib/events/trigger_logout_event.dart @@ -1,3 +1,3 @@ -import 'package:ente_auth/events/event.dart'; +import 'package:ente_events/models/event.dart'; class TriggerLogoutEvent extends Event {} diff --git a/mobile/apps/auth/lib/events/user_details_changed_event.dart b/mobile/apps/auth/lib/events/user_details_changed_event.dart deleted file mode 100644 index 066999f473..0000000000 --- a/mobile/apps/auth/lib/events/user_details_changed_event.dart +++ /dev/null @@ -1,3 +0,0 @@ -import 'package:ente_auth/events/event.dart'; - -class UserDetailsChangedEvent extends Event {} diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 5da7d0a6f9..d67ae1463c 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -15,6 +15,7 @@ import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/services/update_service.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/services/window_listener_service.dart'; +import 'package:ente_auth/store/authenticator_db.dart'; import 'package:ente_auth/store/code_display_store.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/ui/tools/app_lock.dart'; @@ -151,7 +152,7 @@ Future _init(bool bool, {String? via}) async { await PreferenceService.instance.init(); await CodeStore.instance.init(); await CodeDisplayStore.instance.init(); - await Configuration.instance.init(); + await Configuration.instance.init([AuthenticatorDB.instance]); await Network.instance.init(); await UserService.instance.init(); await AuthenticatorService.instance.init(); diff --git a/mobile/apps/auth/lib/models/key_attributes.dart b/mobile/apps/auth/lib/models/key_attributes.dart deleted file mode 100644 index 8b8ec3990f..0000000000 --- a/mobile/apps/auth/lib/models/key_attributes.dart +++ /dev/null @@ -1,102 +0,0 @@ -import 'dart:convert'; - -class KeyAttributes { - final String kekSalt; - final String encryptedKey; - final String keyDecryptionNonce; - final String publicKey; - final String encryptedSecretKey; - final String secretKeyDecryptionNonce; - final int memLimit; - final int opsLimit; - final String masterKeyEncryptedWithRecoveryKey; - final String masterKeyDecryptionNonce; - final String recoveryKeyEncryptedWithMasterKey; - final String recoveryKeyDecryptionNonce; - - KeyAttributes( - this.kekSalt, - this.encryptedKey, - this.keyDecryptionNonce, - this.publicKey, - this.encryptedSecretKey, - this.secretKeyDecryptionNonce, - this.memLimit, - this.opsLimit, - this.masterKeyEncryptedWithRecoveryKey, - this.masterKeyDecryptionNonce, - this.recoveryKeyEncryptedWithMasterKey, - this.recoveryKeyDecryptionNonce, - ); - - Map toMap() { - return { - 'kekSalt': kekSalt, - 'encryptedKey': encryptedKey, - 'keyDecryptionNonce': keyDecryptionNonce, - 'publicKey': publicKey, - 'encryptedSecretKey': encryptedSecretKey, - 'secretKeyDecryptionNonce': secretKeyDecryptionNonce, - 'memLimit': memLimit, - 'opsLimit': opsLimit, - 'masterKeyEncryptedWithRecoveryKey': masterKeyEncryptedWithRecoveryKey, - 'masterKeyDecryptionNonce': masterKeyDecryptionNonce, - 'recoveryKeyEncryptedWithMasterKey': recoveryKeyEncryptedWithMasterKey, - 'recoveryKeyDecryptionNonce': recoveryKeyDecryptionNonce, - }; - } - - factory KeyAttributes.fromMap(Map map) { - return KeyAttributes( - map['kekSalt'], - map['encryptedKey'], - map['keyDecryptionNonce'], - map['publicKey'], - map['encryptedSecretKey'], - map['secretKeyDecryptionNonce'], - map['memLimit'], - map['opsLimit'], - map['masterKeyEncryptedWithRecoveryKey'], - map['masterKeyDecryptionNonce'], - map['recoveryKeyEncryptedWithMasterKey'], - map['recoveryKeyDecryptionNonce'], - ); - } - - String toJson() => json.encode(toMap()); - - factory KeyAttributes.fromJson(String source) => - KeyAttributes.fromMap(json.decode(source)); - - KeyAttributes copyWith({ - String? kekSalt, - String? encryptedKey, - String? keyDecryptionNonce, - String? publicKey, - String? encryptedSecretKey, - String? secretKeyDecryptionNonce, - int? memLimit, - int? opsLimit, - String? masterKeyEncryptedWithRecoveryKey, - String? masterKeyDecryptionNonce, - String? recoveryKeyEncryptedWithMasterKey, - String? recoveryKeyDecryptionNonce, - }) { - return KeyAttributes( - kekSalt ?? this.kekSalt, - encryptedKey ?? this.encryptedKey, - keyDecryptionNonce ?? this.keyDecryptionNonce, - publicKey ?? this.publicKey, - encryptedSecretKey ?? this.encryptedSecretKey, - secretKeyDecryptionNonce ?? this.secretKeyDecryptionNonce, - memLimit ?? this.memLimit, - opsLimit ?? this.opsLimit, - masterKeyEncryptedWithRecoveryKey ?? - this.masterKeyEncryptedWithRecoveryKey, - masterKeyDecryptionNonce ?? this.masterKeyDecryptionNonce, - recoveryKeyEncryptedWithMasterKey ?? - this.recoveryKeyEncryptedWithMasterKey, - recoveryKeyDecryptionNonce ?? this.recoveryKeyDecryptionNonce, - ); - } -} diff --git a/mobile/apps/auth/lib/models/key_gen_result.dart b/mobile/apps/auth/lib/models/key_gen_result.dart deleted file mode 100644 index 5b581b0ab1..0000000000 --- a/mobile/apps/auth/lib/models/key_gen_result.dart +++ /dev/null @@ -1,12 +0,0 @@ -import 'dart:typed_data'; - -import 'package:ente_auth/models/key_attributes.dart'; -import 'package:ente_auth/models/private_key_attributes.dart'; - -class KeyGenResult { - final KeyAttributes keyAttributes; - final PrivateKeyAttributes privateKeyAttributes; - final Uint8List loginKey; - - KeyGenResult(this.keyAttributes, this.privateKeyAttributes, this.loginKey); -} diff --git a/mobile/apps/auth/lib/models/private_key_attributes.dart b/mobile/apps/auth/lib/models/private_key_attributes.dart deleted file mode 100644 index c92f017fc6..0000000000 --- a/mobile/apps/auth/lib/models/private_key_attributes.dart +++ /dev/null @@ -1,7 +0,0 @@ -class PrivateKeyAttributes { - final String key; - final String recoveryKey; - final String secretKey; - - PrivateKeyAttributes(this.key, this.recoveryKey, this.secretKey); -} diff --git a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart index ef5a297392..0ce09dd45c 100644 --- a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart +++ b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart @@ -3,7 +3,6 @@ import 'dart:io'; import 'package:ente_auth/app/view/app.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/events/trigger_logout_event.dart'; import "package:ente_auth/l10n/l10n.dart"; @@ -24,6 +23,7 @@ import 'package:ente_auth/ui/settings/language_picker.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:local_auth/local_auth.dart'; diff --git a/mobile/apps/auth/lib/onboarding/view/setup_enter_secret_key_page.dart b/mobile/apps/auth/lib/onboarding/view/setup_enter_secret_key_page.dart index 0bf16c7e62..3472426cbd 100644 --- a/mobile/apps/auth/lib/onboarding/view/setup_enter_secret_key_page.dart +++ b/mobile/apps/auth/lib/onboarding/view/setup_enter_secret_key_page.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/models/all_icon_data.dart'; @@ -23,6 +22,7 @@ import 'package:ente_auth/ui/utils/icon_utils.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; +import 'package:ente_events/event_bus.dart'; import "package:flutter/material.dart"; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/services/authenticator_service.dart b/mobile/apps/auth/lib/services/authenticator_service.dart index 559e85f81a..77f814d828 100644 --- a/mobile/apps/auth/lib/services/authenticator_service.dart +++ b/mobile/apps/auth/lib/services/authenticator_service.dart @@ -4,9 +4,7 @@ import 'dart:math'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; -import 'package:ente_auth/events/signed_in_event.dart'; import 'package:ente_auth/events/trigger_logout_event.dart'; import 'package:ente_auth/gateway/authenticator.dart'; import 'package:ente_auth/models/authenticator/auth_entity.dart'; @@ -17,6 +15,8 @@ import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/store/authenticator_db.dart'; import 'package:ente_auth/store/offline_authenticator_db.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/signed_in_event.dart'; import 'package:flutter/foundation.dart'; import 'package:logging/logging.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/mobile/apps/auth/lib/services/preference_service.dart b/mobile/apps/auth/lib/services/preference_service.dart index 773ee1b149..fd3b6d68c7 100644 --- a/mobile/apps/auth/lib/services/preference_service.dart +++ b/mobile/apps/auth/lib/services/preference_service.dart @@ -1,5 +1,5 @@ -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/icons_changed_event.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:shared_preferences/shared_preferences.dart'; enum CodeSortKey { diff --git a/mobile/apps/auth/lib/services/user_service.dart b/mobile/apps/auth/lib/services/user_service.dart index 0326023efa..b4dd7eb55a 100644 --- a/mobile/apps/auth/lib/services/user_service.dart +++ b/mobile/apps/auth/lib/services/user_service.dart @@ -7,15 +7,11 @@ import 'package:dio/dio.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/core/network.dart'; -import 'package:ente_auth/events/user_details_changed_event.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/account/two_factor.dart'; import 'package:ente_auth/models/api/user/srp.dart'; import 'package:ente_auth/models/delete_account.dart'; -import 'package:ente_auth/models/key_attributes.dart'; -import 'package:ente_auth/models/key_gen_result.dart'; import 'package:ente_auth/models/sessions.dart'; import 'package:ente_auth/models/set_keys_request.dart'; import 'package:ente_auth/models/set_recovery_key_request.dart'; @@ -32,7 +28,11 @@ import 'package:ente_auth/ui/two_factor_authentication_page.dart'; import 'package:ente_auth/ui/two_factor_recovery_page.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/store/authenticator_db.dart b/mobile/apps/auth/lib/store/authenticator_db.dart index cc47745d59..8e8ac11bb0 100644 --- a/mobile/apps/auth/lib/store/authenticator_db.dart +++ b/mobile/apps/auth/lib/store/authenticator_db.dart @@ -4,13 +4,14 @@ import 'dart:io'; import 'package:ente_auth/models/authenticator/auth_entity.dart'; import 'package:ente_auth/models/authenticator/local_auth_entity.dart'; import 'package:ente_auth/utils/directory_utils.dart'; +import 'package:ente_base/ente_base.dart'; import 'package:flutter/foundation.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; import 'package:sqflite/sqflite.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; -class AuthenticatorDB { +class AuthenticatorDB extends EnteBaseDatabase { static const _databaseName = "ente.authenticator.db"; static const _databaseVersion = 1; diff --git a/mobile/apps/auth/lib/store/code_store.dart b/mobile/apps/auth/lib/store/code_store.dart index e6f984f3eb..8abb5657a4 100644 --- a/mobile/apps/auth/lib/store/code_store.dart +++ b/mobile/apps/auth/lib/store/code_store.dart @@ -3,12 +3,12 @@ import 'dart:convert'; import 'package:collection/collection.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; import 'package:ente_auth/models/authenticator/entity_result.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/store/offline_authenticator_db.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:logging/logging.dart'; class CodeStore { diff --git a/mobile/apps/auth/lib/ui/account/password_entry_page.dart b/mobile/apps/auth/lib/ui/account/password_entry_page.dart index b7e3e14694..acaf051197 100644 --- a/mobile/apps/auth/lib/ui/account/password_entry_page.dart +++ b/mobile/apps/auth/lib/ui/account/password_entry_page.dart @@ -1,6 +1,5 @@ import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/key_gen_result.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/account/recovery_key_page.dart'; import 'package:ente_auth/ui/common/dynamic_fab.dart'; @@ -451,7 +450,7 @@ class _PasswordEntryPageState extends State { if (usingVolatilePassword) { _logger.info('Using volatile password'); } - final KeyGenResult result = + final result = await Configuration.instance.generateKey(password); Configuration.instance.resetVolatilePassword(); await dialog.hide(); diff --git a/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart b/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart index 52d97bf26b..9583db2757 100644 --- a/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart +++ b/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart @@ -1,10 +1,10 @@ import 'dart:ui'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/utils/platform_util.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:flutter/material.dart'; class CoachMarkWidget extends StatelessWidget { diff --git a/mobile/apps/auth/lib/ui/home_page.dart b/mobile/apps/auth/lib/ui/home_page.dart index 3a3d650259..e218e4fbda 100644 --- a/mobile/apps/auth/lib/ui/home_page.dart +++ b/mobile/apps/auth/lib/ui/home_page.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:app_links/app_links.dart'; import 'package:collection/collection.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; import 'package:ente_auth/events/icons_changed_event.dart'; @@ -39,6 +38,7 @@ import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/lock_screen_settings.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/general_section_widget.dart b/mobile/apps/auth/lib/ui/settings/general_section_widget.dart index aadb974b24..8475f45c3a 100644 --- a/mobile/apps/auth/lib/ui/settings/general_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/general_section_widget.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:ente_auth/app/view/app.dart'; -import 'package:ente_auth/core/event_bus.dart'; import 'package:ente_auth/events/icons_changed_event.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/locale.dart'; @@ -15,6 +14,7 @@ import 'package:ente_auth/ui/settings/common_settings.dart'; import 'package:ente_auth/ui/settings/language_picker.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; +import 'package:ente_events/event_bus.dart'; import 'package:ente_logging/logging.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/apps/auth/lib/utils/lock_screen_settings.dart b/mobile/apps/auth/lib/utils/lock_screen_settings.dart index 247f7ae4b2..4f2f81b11d 100644 --- a/mobile/apps/auth/lib/utils/lock_screen_settings.dart +++ b/mobile/apps/auth/lib/utils/lock_screen_settings.dart @@ -3,10 +3,10 @@ import "dart:io"; import "dart:typed_data"; import "package:ente_auth/core/configuration.dart"; -import "package:ente_auth/core/event_bus.dart"; -import "package:ente_auth/events/signed_out_event.dart"; import "package:ente_auth/utils/platform_util.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_events/event_bus.dart"; +import "package:ente_events/models/signed_out_event.dart"; import "package:flutter/material.dart"; import "package:flutter_secure_storage/flutter_secure_storage.dart"; import "package:privacy_screen/privacy_screen.dart"; diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index c8725a89b1..82838413d6 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -398,6 +398,20 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + ente_base: + dependency: "direct main" + description: + path: "../../packages/base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../../packages/configuration" + relative: true + source: path + version: "1.0.0" ente_crypto_dart: dependency: "direct main" description: @@ -407,6 +421,13 @@ packages: url: "https://github.com/ente-io/ente_crypto_dart.git" source: git version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../../packages/events" + relative: true + source: path + version: "1.0.0" ente_logging: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index ed015d040b..bc8695a26d 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -25,9 +25,15 @@ dependencies: dotted_border: ^2.0.0+2 dropdown_button2: ^2.3.9 email_validator: ^3.0.0 + ente_base: + path: ../../packages/base + ente_configuration: + path: ../../packages/configuration ente_crypto_dart: git: url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../../packages/events ente_logging: path: ../../packages/logging event_bus: ^2.0.0 diff --git a/mobile/packages/configuration/lib/base_configuration.dart b/mobile/packages/configuration/lib/base_configuration.dart index 75acb8e687..5dfc722834 100644 --- a/mobile/packages/configuration/lib/base_configuration.dart +++ b/mobile/packages/configuration/lib/base_configuration.dart @@ -2,7 +2,6 @@ library configuration; import 'dart:convert'; import 'dart:io' as io; -import 'dart:typed_data'; import 'package:bip39/bip39.dart' as bip39; import 'package:ente_configuration/constants.dart'; @@ -16,32 +15,26 @@ import 'package:ente_events/models/endpoint_updated_event.dart'; import 'package:ente_events/models/signed_in_event.dart'; import 'package:ente_events/models/signed_out_event.dart'; import 'package:ente_logging/logging.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tuple/tuple.dart'; class BaseConfiguration { - BaseConfiguration._privateConstructor(); - - static final BaseConfiguration instance = - BaseConfiguration._privateConstructor(); static const endpoint = String.fromEnvironment( "endpoint", defaultValue: kDefaultProductionEndpoint, ); static const emailKey = "email"; static const keyAttributesKey = "key_attributes"; - - static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time"; static const keyKey = "key"; static const secretKeyKey = "secret_key"; - static const authSecretKeyKey = "auth_secret_key"; - static const offlineAuthSecretKey = "offline_auth_secret_key"; static const tokenKey = "token"; static const encryptedTokenKey = "encrypted_token"; static const userIDKey = "user_id"; static const endPointKey = "endpoint"; + static const lastTempFolderClearTimeKey = "last_temp_folder_clear_time"; final kTempFolderDeletionTimeBuffer = const Duration(days: 1).inMicroseconds; @@ -69,27 +62,8 @@ class BaseConfiguration { accessibility: KeychainAccessibility.first_unlock_this_device, ), ); - final tempDirectory = io.Directory(_tempDocumentsDirPath); - try { - final currentTime = DateTime.now().microsecondsSinceEpoch; - if (tempDirectory.existsSync() && - (_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) < - (currentTime - kTempFolderDeletionTimeBuffer)) { - await tempDirectory.delete(recursive: true); - await _preferences.setInt(lastTempFolderClearTimeKey, currentTime); - _logger.info("Cleared temp folder"); - } else { - _logger.info("Skipping temp folder clear"); - } - } catch (e) { - _logger.warning(e); - } - tempDirectory.createSync(recursive: true); - - _cacheDirectory = "$_documentsDirectory/cache/"; - if (!io.Directory(_cacheDirectory).existsSync()) { - io.Directory(_cacheDirectory).createSync(recursive: true); - } + _setupKeys(); + _setupFolders(); } Future logout({bool autoLogout = false}) async { @@ -404,4 +378,61 @@ class BaseConfiguration { String? getVolatilePassword() { return _volatilePassword; } + + Future _setupKeys() async { + try { + if (!_preferences.containsKey(tokenKey)) { + await _secureStorage.deleteAll(); + return; + } + _key = await _secureStorage.read(key: keyKey); + if (_key == null) { + _logger.warning("No key found in secure storage"); + await logout(autoLogout: true); + } + _secretKey = await _secureStorage.read(key: secretKeyKey); + } catch (e, s) { + _logger.severe("Configuration init failed", e, s); + /* + Check if it's a known is related to reading secret from secure storage + on android https://github.com/mogol/flutter_secure_storage/issues/541 + */ + if (e is PlatformException) { + final PlatformException error = e; + final bool isBadPaddingError = + error.toString().contains('BadPaddingException') || + (error.message ?? '').contains('BadPaddingException'); + if (isBadPaddingError) { + await logout(autoLogout: true); + return; + } + } else { + rethrow; + } + } + } + + Future _setupFolders() async { + final tempDirectory = io.Directory(_tempDocumentsDirPath); + try { + final currentTime = DateTime.now().microsecondsSinceEpoch; + if (tempDirectory.existsSync() && + (_preferences.getInt(lastTempFolderClearTimeKey) ?? 0) < + (currentTime - kTempFolderDeletionTimeBuffer)) { + await tempDirectory.delete(recursive: true); + await _preferences.setInt(lastTempFolderClearTimeKey, currentTime); + _logger.info("Cleared temp folder"); + } else { + _logger.info("Skipping temp folder clear"); + } + } catch (e) { + _logger.warning(e); + } + tempDirectory.createSync(recursive: true); + + _cacheDirectory = "$_documentsDirectory/cache/"; + if (!io.Directory(_cacheDirectory).existsSync()) { + io.Directory(_cacheDirectory).createSync(recursive: true); + } + } } From a7d0e2eef56efb079190935ebece44d5626b088b Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 14:58:55 +0530 Subject: [PATCH 012/164] Refactor network --- mobile/packages/network/README.md | 37 + mobile/packages/network/lib/ente_network.dart | 3 + mobile/packages/network/lib/network.dart | 111 +++ mobile/packages/network/pubspec.lock | 799 ++++++++++++++++++ mobile/packages/network/pubspec.yaml | 28 + 5 files changed, 978 insertions(+) create mode 100644 mobile/packages/network/README.md create mode 100644 mobile/packages/network/lib/ente_network.dart create mode 100644 mobile/packages/network/lib/network.dart create mode 100644 mobile/packages/network/pubspec.lock create mode 100644 mobile/packages/network/pubspec.yaml diff --git a/mobile/packages/network/README.md b/mobile/packages/network/README.md new file mode 100644 index 0000000000..9311f94cc8 --- /dev/null +++ b/mobile/packages/network/README.md @@ -0,0 +1,37 @@ +# Ente Network + +A Flutter package for network management and HTTP client configuration used across Ente applications. + +## Features + +- Configurable HTTP client using Dio +- Request interceptors for authentication and request tracking +- Platform-aware user agent handling +- Connection timeout management +- Base URL configuration +- Request ID generation + +## Usage + +```dart +import 'package:ente_network/network.dart'; +import 'package:ente_configuration/base_configuration.dart'; + +// Initialize the network service +await Network.instance.init(configuration); + +// Use the configured Dio instances +final dio = Network.instance.getDio(); +final enteDio = Network.instance.enteDio; +``` + +## Dependencies + +This package depends on: +- `dio` for HTTP client functionality +- `ente_configuration` for configuration management +- `ente_events` for event handling +- `native_dio_adapter` for native networking +- `package_info_plus` for package information +- `ua_client_hints` for user agent generation +- `uuid` for request ID generation diff --git a/mobile/packages/network/lib/ente_network.dart b/mobile/packages/network/lib/ente_network.dart new file mode 100644 index 0000000000..d269ce93e5 --- /dev/null +++ b/mobile/packages/network/lib/ente_network.dart @@ -0,0 +1,3 @@ +library ente_network; + +export 'network.dart'; diff --git a/mobile/packages/network/lib/network.dart b/mobile/packages/network/lib/network.dart new file mode 100644 index 0000000000..b95c4b5a8f --- /dev/null +++ b/mobile/packages/network/lib/network.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/endpoint_updated_event.dart'; +import 'package:flutter/foundation.dart'; +import 'package:native_dio_adapter/native_dio_adapter.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:ua_client_hints/ua_client_hints.dart'; +import 'package:uuid/uuid.dart'; + +int kConnectTimeout = 15000; + +class Network { + late Dio _dio; + late Dio _enteDio; + + Future init(BaseConfiguration configuration) async { + final String ua = await userAgent(); + final packageInfo = await PackageInfo.fromPlatform(); + final version = packageInfo.version; + final packageName = packageInfo.packageName; + final endpoint = configuration.getHttpEndpoint(); + final isMobile = Platform.isAndroid || Platform.isIOS; + + _dio = Dio( + BaseOptions( + connectTimeout: Duration(milliseconds: kConnectTimeout), + headers: { + HttpHeaders.userAgentHeader: isMobile ? ua : Platform.operatingSystem, + 'X-Client-Version': version, + 'X-Client-Package': packageName, + }, + ), + ); + + _enteDio = Dio( + BaseOptions( + baseUrl: endpoint, + connectTimeout: Duration(milliseconds: kConnectTimeout), + headers: { + if (isMobile) + HttpHeaders.userAgentHeader: ua + else + HttpHeaders.userAgentHeader: Platform.operatingSystem, + 'X-Client-Version': version, + 'X-Client-Package': packageName, + }, + ), + ); + + _dio.httpClientAdapter = NativeAdapter(); + _enteDio.httpClientAdapter = NativeAdapter(); + + _setupInterceptors(configuration); + + Bus.instance.on().listen((event) { + final endpoint = configuration.getHttpEndpoint(); + _enteDio.options.baseUrl = endpoint; + _setupInterceptors(configuration); + }); + } + + Network._privateConstructor(); + + static Network instance = Network._privateConstructor(); + + Dio getDio() => _dio; + Dio get enteDio => _enteDio; + + void _setupInterceptors(BaseConfiguration configuration) { + _dio.interceptors.clear(); + _dio.interceptors.add(RequestIdInterceptor()); + + _enteDio.interceptors.clear(); + _enteDio.interceptors.add(EnteRequestInterceptor(configuration)); + } +} + +class RequestIdInterceptor extends Interceptor { + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + options.headers + .putIfAbsent("x-request-id", () => const Uuid().v4().toString()); + return super.onRequest(options, handler); + } +} + +class EnteRequestInterceptor extends Interceptor { + final BaseConfiguration configuration; + + EnteRequestInterceptor(this.configuration); + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (kDebugMode) { + assert( + options.baseUrl == configuration.getHttpEndpoint(), + "interceptor should only be used for API endpoint", + ); + } + options.headers + .putIfAbsent("x-request-id", () => const Uuid().v4().toString()); + final String? tokenValue = configuration.getToken(); + if (tokenValue != null) { + options.headers.putIfAbsent("X-Auth-Token", () => tokenValue); + } + return super.onRequest(options, handler); + } +} diff --git a/mobile/packages/network/pubspec.lock b/mobile/packages/network/pubspec.lock new file mode 100644 index 0000000000..93c404e894 --- /dev/null +++ b/mobile/packages/network/pubspec.lock @@ -0,0 +1,799 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: transitive + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: df26af0de7c4eff46c53c190b5590e22457bfce6ea679aedb1e6326197f27d6f + url: "https://pub.dev" + source: hosted + version: "1.4.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + ente_base: + dependency: transitive + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: transitive + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + jni: + dependency: transitive + description: + name: jni + sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 + url: "https://pub.dev" + source: hosted + version: "0.14.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + native_dio_adapter: + dependency: "direct main" + description: + name: native_dio_adapter + sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + ua_client_hints: + dependency: "direct main" + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/network/pubspec.yaml b/mobile/packages/network/pubspec.yaml new file mode 100644 index 0000000000..bdcf9f9868 --- /dev/null +++ b/mobile/packages/network/pubspec.yaml @@ -0,0 +1,28 @@ +name: ente_network +description: A Flutter package for network management and HTTP client configuration +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + dio: ^5.8.0+1 + ente_configuration: + path: ../../packages/configuration + ente_events: + path: ../../packages/events + native_dio_adapter: ^1.4.0 + package_info_plus: ^8.3.0 + ua_client_hints: ^1.4.1 + uuid: ^4.5.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From 13ed1e76bc6c00ba558d6152e304fe25d319e425 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 15:04:46 +0530 Subject: [PATCH 013/164] Use common network package --- mobile/apps/auth/lib/core/network.dart | 113 ------------------ .../apps/auth/lib/gateway/authenticator.dart | 2 +- mobile/apps/auth/lib/main.dart | 4 +- .../auth/lib/services/billing_service.dart | 2 +- .../auth/lib/services/passkey_service.dart | 2 +- .../auth/lib/services/update_service.dart | 2 +- .../apps/auth/lib/services/user_service.dart | 2 +- mobile/apps/auth/pubspec.lock | 17 ++- mobile/apps/auth/pubspec.yaml | 2 + 9 files changed, 25 insertions(+), 121 deletions(-) delete mode 100644 mobile/apps/auth/lib/core/network.dart diff --git a/mobile/apps/auth/lib/core/network.dart b/mobile/apps/auth/lib/core/network.dart deleted file mode 100644 index 9e884fa159..0000000000 --- a/mobile/apps/auth/lib/core/network.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/utils/package_info_util.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/endpoint_updated_event.dart'; -import 'package:fk_user_agent/fk_user_agent.dart'; -import 'package:flutter/foundation.dart'; -import 'package:native_dio_adapter/native_dio_adapter.dart'; -import 'package:uuid/uuid.dart'; - -int kConnectTimeout = 15000; - -class Network { - late Dio _dio; - late Dio _enteDio; - - Future init() async { - if (PlatformUtil.isMobile()) await FkUserAgent.init(); - final packageInfo = await PackageInfoUtil().getPackageInfo(); - final version = PackageInfoUtil().getVersion(packageInfo); - final packageName = PackageInfoUtil().getPackageName(packageInfo); - final endpoint = Configuration.instance.getHttpEndpoint(); - - _dio = Dio( - BaseOptions( - connectTimeout: Duration(milliseconds: kConnectTimeout), - headers: { - HttpHeaders.userAgentHeader: PlatformUtil.isMobile() - ? FkUserAgent.userAgent - : Platform.operatingSystem, - 'X-Client-Version': version, - 'X-Client-Package': packageName, - }, - ), - ); - - _enteDio = Dio( - BaseOptions( - baseUrl: endpoint, - connectTimeout: Duration(milliseconds: kConnectTimeout), - headers: { - if (PlatformUtil.isMobile()) - HttpHeaders.userAgentHeader: FkUserAgent.userAgent - else - HttpHeaders.userAgentHeader: Platform.operatingSystem, - 'X-Client-Version': version, - 'X-Client-Package': packageName, - }, - ), - ); - - _dio.httpClientAdapter = NativeAdapter(); - _enteDio.httpClientAdapter = NativeAdapter(); - - _setupInterceptors(endpoint); - - Bus.instance.on().listen((event) { - final endpoint = Configuration.instance.getHttpEndpoint(); - _enteDio.options.baseUrl = endpoint; - _setupInterceptors(endpoint); - }); - } - - Network._privateConstructor(); - - static Network instance = Network._privateConstructor(); - - Dio getDio() => _dio; - Dio get enteDio => _enteDio; - - void _setupInterceptors(String endpoint) { - _dio.interceptors.clear(); - _dio.interceptors.add(RequestIdInterceptor()); - - _enteDio.interceptors.clear(); - _enteDio.interceptors.add(EnteRequestInterceptor(endpoint)); - } -} - -class RequestIdInterceptor extends Interceptor { - @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - options.headers - .putIfAbsent("x-request-id", () => const Uuid().v4().toString()); - return super.onRequest(options, handler); - } -} - -class EnteRequestInterceptor extends Interceptor { - final String endpoint; - - EnteRequestInterceptor(this.endpoint); - - @override - void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - if (kDebugMode) { - assert( - options.baseUrl == endpoint, - "interceptor should only be used for API endpoint", - ); - } - options.headers - .putIfAbsent("x-request-id", () => const Uuid().v4().toString()); - final String? tokenValue = Configuration.instance.getToken(); - if (tokenValue != null) { - options.headers.putIfAbsent("X-Auth-Token", () => tokenValue); - } - return super.onRequest(options, handler); - } -} diff --git a/mobile/apps/auth/lib/gateway/authenticator.dart b/mobile/apps/auth/lib/gateway/authenticator.dart index 1375146e48..fa0d595b55 100644 --- a/mobile/apps/auth/lib/gateway/authenticator.dart +++ b/mobile/apps/auth/lib/gateway/authenticator.dart @@ -1,8 +1,8 @@ import 'package:dio/dio.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/models/authenticator/auth_entity.dart'; import 'package:ente_auth/models/authenticator/auth_key.dart'; +import 'package:ente_network/network.dart'; class AuthenticatorGateway { late Dio _enteDio; diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index d67ae1463c..29c8279abe 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -5,7 +5,6 @@ import 'package:adaptive_theme/adaptive_theme.dart'; import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/services/authenticator_service.dart'; @@ -27,6 +26,7 @@ import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/window_protocol_handler.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_logging/logging.dart'; +import 'package:ente_network/network.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_displaymode/flutter_displaymode.dart'; @@ -153,7 +153,7 @@ Future _init(bool bool, {String? via}) async { await CodeStore.instance.init(); await CodeDisplayStore.instance.init(); await Configuration.instance.init([AuthenticatorDB.instance]); - await Network.instance.init(); + await Network.instance.init(Configuration.instance); await UserService.instance.init(); await AuthenticatorService.instance.init(); await BillingService.instance.init(); diff --git a/mobile/apps/auth/lib/services/billing_service.dart b/mobile/apps/auth/lib/services/billing_service.dart index 363425cd53..39929dc5aa 100644 --- a/mobile/apps/auth/lib/services/billing_service.dart +++ b/mobile/apps/auth/lib/services/billing_service.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/models/billing_plan.dart'; import 'package:ente_auth/models/subscription.dart'; +import 'package:ente_network/network.dart'; import 'package:logging/logging.dart'; const kWebPaymentRedirectUrl = "https://payments.ente.io/frameRedirect"; diff --git a/mobile/apps/auth/lib/services/passkey_service.dart b/mobile/apps/auth/lib/services/passkey_service.dart index dd5a4e5f7a..20fd037f3f 100644 --- a/mobile/apps/auth/lib/services/passkey_service.dart +++ b/mobile/apps/auth/lib/services/passkey_service.dart @@ -1,6 +1,6 @@ import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/utils/dialog_util.dart'; +import 'package:ente_network/network.dart'; import 'package:flutter/widgets.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/apps/auth/lib/services/update_service.dart b/mobile/apps/auth/lib/services/update_service.dart index 716d553f1b..625c646556 100644 --- a/mobile/apps/auth/lib/services/update_service.dart +++ b/mobile/apps/auth/lib/services/update_service.dart @@ -2,9 +2,9 @@ import 'dart:async'; import 'dart:io'; import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/services/notification_service.dart'; import 'package:ente_auth/utils/platform_util.dart'; +import 'package:ente_network/network.dart'; import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/mobile/apps/auth/lib/services/user_service.dart b/mobile/apps/auth/lib/services/user_service.dart index b4dd7eb55a..2becb8e590 100644 --- a/mobile/apps/auth/lib/services/user_service.dart +++ b/mobile/apps/auth/lib/services/user_service.dart @@ -7,7 +7,6 @@ import 'package:dio/dio.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/core/network.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/account/two_factor.dart'; import 'package:ente_auth/models/api/user/srp.dart'; @@ -33,6 +32,7 @@ import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_events/event_bus.dart'; import 'package:ente_events/models/user_details_changed_event.dart'; +import 'package:ente_network/network.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 82838413d6..b670dcb123 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -435,6 +435,13 @@ packages: relative: true source: path version: "1.0.0" + ente_network: + dependency: "direct main" + description: + path: "../../packages/network" + relative: true + source: path + version: "1.0.0" event_bus: dependency: "direct main" description: @@ -1716,7 +1723,15 @@ packages: sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" + ua_client_hints: + dependency: transitive + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" universal_io: dependency: transitive description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index bc8695a26d..6cabe14406 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -36,6 +36,8 @@ dependencies: path: ../../packages/events ente_logging: path: ../../packages/logging + ente_network: + path: ../../packages/network event_bus: ^2.0.0 expandable: ^5.0.1 expansion_tile_card: ^3.0.0 From 150513d3e51b4625b8a042850a829b21c4cf1f55 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 19:45:22 +0530 Subject: [PATCH 014/164] Refactor common strings --- mobile/packages/strings/CHANGELOG.md | 41 ++ mobile/packages/strings/README.md | 95 +++++ mobile/packages/strings/analysis_options.yaml | 6 + mobile/packages/strings/build/.last_build_id | 1 + .../.filecache | 1 + .../gen_l10n_inputs_and_outputs.json | 1 + .../gen_localizations.d | 1 + .../gen_localizations.stamp | 1 + .../outputs.json | 1 + mobile/packages/strings/example/main.dart | 90 ++++ mobile/packages/strings/example/pubspec.lock | 225 ++++++++++ mobile/packages/strings/example/pubspec.yaml | 22 + mobile/packages/strings/l10n.yaml | 6 + mobile/packages/strings/lib/ente_strings.dart | 5 + mobile/packages/strings/lib/extensions.dart | 11 + .../strings/lib/l10n/arb/strings_ar.arb | 9 + .../strings/lib/l10n/arb/strings_bg.arb | 3 + .../strings/lib/l10n/arb/strings_cs.arb | 3 + .../strings/lib/l10n/arb/strings_da.arb | 3 + .../strings/lib/l10n/arb/strings_el.arb | 3 + .../strings/lib/l10n/arb/strings_en.arb | 110 +++++ .../strings/lib/l10n/arb/strings_es.arb | 9 + .../strings/lib/l10n/arb/strings_fr.arb | 9 + .../strings/lib/l10n/arb/strings_id.arb | 3 + .../strings/lib/l10n/arb/strings_ja.arb | 9 + .../strings/lib/l10n/arb/strings_ko.arb | 3 + .../strings/lib/l10n/arb/strings_lt.arb | 3 + .../strings/lib/l10n/arb/strings_nl.arb | 3 + .../strings/lib/l10n/arb/strings_pl.arb | 3 + .../strings/lib/l10n/arb/strings_pt.arb | 3 + .../strings/lib/l10n/arb/strings_ru.arb | 3 + .../strings/lib/l10n/arb/strings_sk.arb | 3 + .../strings/lib/l10n/arb/strings_sr.arb | 3 + .../strings/lib/l10n/arb/strings_sv.arb | 3 + .../strings/lib/l10n/arb/strings_tr.arb | 3 + .../strings/lib/l10n/arb/strings_vi.arb | 3 + .../strings/lib/l10n/arb/strings_zh.arb | 3 + .../strings/lib/l10n/arb/strings_zh_TW.arb | 3 + .../lib/l10n/strings_localizations.dart | 394 ++++++++++++++++++ .../lib/l10n/strings_localizations_ar.dart | 90 ++++ .../lib/l10n/strings_localizations_bg.dart | 90 ++++ .../lib/l10n/strings_localizations_cs.dart | 90 ++++ .../lib/l10n/strings_localizations_da.dart | 90 ++++ .../lib/l10n/strings_localizations_el.dart | 90 ++++ .../lib/l10n/strings_localizations_en.dart | 90 ++++ .../lib/l10n/strings_localizations_es.dart | 90 ++++ .../lib/l10n/strings_localizations_fr.dart | 90 ++++ .../lib/l10n/strings_localizations_id.dart | 90 ++++ .../lib/l10n/strings_localizations_ja.dart | 90 ++++ .../lib/l10n/strings_localizations_ko.dart | 90 ++++ .../lib/l10n/strings_localizations_lt.dart | 90 ++++ .../lib/l10n/strings_localizations_nl.dart | 90 ++++ .../lib/l10n/strings_localizations_pl.dart | 90 ++++ .../lib/l10n/strings_localizations_pt.dart | 90 ++++ .../lib/l10n/strings_localizations_ru.dart | 90 ++++ .../lib/l10n/strings_localizations_sk.dart | 90 ++++ .../lib/l10n/strings_localizations_sr.dart | 90 ++++ .../lib/l10n/strings_localizations_sv.dart | 90 ++++ .../lib/l10n/strings_localizations_tr.dart | 90 ++++ .../lib/l10n/strings_localizations_vi.dart | 90 ++++ .../lib/l10n/strings_localizations_zh.dart | 97 +++++ mobile/packages/strings/pubspec.lock | 218 ++++++++++ mobile/packages/strings/pubspec.yaml | 22 + .../packages/strings/test/strings_test.dart | 32 ++ 64 files changed, 3360 insertions(+) create mode 100644 mobile/packages/strings/CHANGELOG.md create mode 100644 mobile/packages/strings/README.md create mode 100644 mobile/packages/strings/analysis_options.yaml create mode 100644 mobile/packages/strings/build/.last_build_id create mode 100644 mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache create mode 100644 mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json create mode 100644 mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d create mode 100644 mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp create mode 100644 mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json create mode 100644 mobile/packages/strings/example/main.dart create mode 100644 mobile/packages/strings/example/pubspec.lock create mode 100644 mobile/packages/strings/example/pubspec.yaml create mode 100644 mobile/packages/strings/l10n.yaml create mode 100644 mobile/packages/strings/lib/ente_strings.dart create mode 100644 mobile/packages/strings/lib/extensions.dart create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ar.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_bg.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_cs.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_da.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_el.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_en.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_es.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_fr.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_id.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ja.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ko.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_lt.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_nl.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_pl.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_pt.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ru.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_sk.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_sr.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_sv.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_tr.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_vi.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_zh.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ar.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_bg.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_cs.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_da.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_el.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_en.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_es.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_fr.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_id.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ja.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ko.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_lt.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_nl.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_pl.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_pt.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ru.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_sk.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_sr.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_sv.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_tr.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_vi.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_zh.dart create mode 100644 mobile/packages/strings/pubspec.lock create mode 100644 mobile/packages/strings/pubspec.yaml create mode 100644 mobile/packages/strings/test/strings_test.dart diff --git a/mobile/packages/strings/CHANGELOG.md b/mobile/packages/strings/CHANGELOG.md new file mode 100644 index 0000000000..b30256ddba --- /dev/null +++ b/mobile/packages/strings/CHANGELOG.md @@ -0,0 +1,41 @@ +# Changelog + +All notable changes to the `ente_strings` package will be documented in this file. + +## [1.0.0] - 2025-07-19 + +### Added +- Initial release of the `ente_strings` package +- Centralized localization for common strings across Ente apps +- Support for 22 languages with `networkHostLookUpErr` string: + - Arabic (ar) + - Bulgarian (bg) + - Czech (cs) + - Danish (da) + - Greek (el) + - English (en) + - French (fr) + - Indonesian (id) + - Japanese (ja) + - Korean (ko) + - Lithuanian (lt) + - Dutch (nl) + - Polish (pl) + - Portuguese (pt) + - Russian (ru) + - Slovak (sk) + - Serbian (sr) + - Swedish (sv) + - Turkish (tr) + - Vietnamese (vi) + - Chinese Simplified (zh) + - Chinese Traditional (zh_TW) +- Convenient `EnteStringsExtension` for easy access via `context.strings` +- Complete documentation and example app +- Unit tests for localization functionality + +### Features +- Flutter localization generation support +- Integration with `flutter_localizations` +- Type-safe string access +- Support for both extension and traditional localization access patterns diff --git a/mobile/packages/strings/README.md b/mobile/packages/strings/README.md new file mode 100644 index 0000000000..9830f72753 --- /dev/null +++ b/mobile/packages/strings/README.md @@ -0,0 +1,95 @@ +# Ente Strings + +A Flutter package containing shared localization strings for Ente apps. + +## Purpose + +This package provides common localization strings that are shared across multiple Ente applications (Auth, Photos, etc.). It centralizes the translations for common UI elements, error messages, and other shared text to ensure consistency across the apps. + +## Usage + +### 1. Add to pubspec.yaml + +```yaml +dependencies: + ente_strings: + path: ../packages/strings +``` + +### 2. Configure in your app + +Add the strings localizations delegate to your app: + +```dart +import 'package:ente_strings/ente_strings.dart'; + +MaterialApp( + localizationsDelegates: [ + ...StringsLocalizations.localizationsDelegates, + // Your other delegates... + ], + supportedLocales: StringsLocalizations.supportedLocales, + // ... +) +``` + +### 3. Use in your widgets + +Use the convenient extension to access strings: + +```dart +import 'package:ente_strings/ente_strings.dart'; + +Widget build(BuildContext context) { + return Text(context.strings.networkHostLookUpErr); +} +``` + +Or use the traditional approach: + +```dart +import 'package:ente_strings/ente_strings.dart'; + +Widget build(BuildContext context) { + return Text(StringsLocalizations.of(context).networkHostLookUpErr); +} +``` + +## Available Strings + +Currently available strings: + +- `networkHostLookUpErr`: Error message for network host lookup failures + +## Adding New Strings + +1. Add the string to `lib/l10n/arb/strings_en.arb` (template file) +2. Add translations to all other `strings_*.arb` files +3. Run `flutter gen-l10n` to regenerate the localization files +4. Move generated files from `lib/l10n/arb/` to `lib/l10n/` if needed + +## Supported Languages + +Currently supported languages include: +- Arabic (ar) +- Bulgarian (bg) +- Czech (cs) +- Danish (da) +- Greek (el) +- English (en) +- French (fr) +- Indonesian (id) +- Japanese (ja) +- Korean (ko) +- Lithuanian (lt) +- Dutch (nl) +- Polish (pl) +- Portuguese (pt) +- Russian (ru) +- Slovak (sk) +- Serbian (sr) +- Swedish (sv) +- Turkish (tr) +- Vietnamese (vi) +- Chinese Simplified (zh) +- Chinese Traditional (zh_TW) diff --git a/mobile/packages/strings/analysis_options.yaml b/mobile/packages/strings/analysis_options.yaml new file mode 100644 index 0000000000..4d72a29c19 --- /dev/null +++ b/mobile/packages/strings/analysis_options.yaml @@ -0,0 +1,6 @@ +include: package:flutter_lints/flutter.yaml + +# Additional rules for Flutter packages +linter: + rules: + public_member_api_docs: false diff --git a/mobile/packages/strings/build/.last_build_id b/mobile/packages/strings/build/.last_build_id new file mode 100644 index 0000000000..da9ac48806 --- /dev/null +++ b/mobile/packages/strings/build/.last_build_id @@ -0,0 +1 @@ +1bb2a21ff13ffb469b0dfcac2f14d315 \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache new file mode 100644 index 0000000000..2ae62e15bc --- /dev/null +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache @@ -0,0 +1 @@ +{"version":2,"files":[{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","hash":"ca7ff33b509a7fd08bf183c17865121b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","hash":"996c26108244f5fbcd0d85755ef80bbb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"45c460142089a4ea87608b06ff2628b1"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","hash":"6c3eaaefdb61387a8fb59ff1382628f4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","hash":"8fb8b9881f5c574f6146928e628d8e2b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"25dad41ccf2f92d2c497c7800a1ce45c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"4f2fe3f8c87899e023e72b5a170061aa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","hash":"afefc4f6503e6789d82deeed4c1090e5"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"09e4f926e05cee727e7f9a32cc98b812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","hash":"efd0836034965b6a58b81068b45b2fdb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"7ec1b1400c0b392a9e261e13ac82656f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"5a8a246b080e3003cf8f728d2b10b6f9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","hash":"b1ca0d86c7506706e244476e7017c1a8"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"24b4e737f1bef65c324059937e559819"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","hash":"cb3e5ffb84b723fd099f6291ac5374ef"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","hash":"3aecb1a3a5ea764716303c278ceb94b7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","hash":"8175296d43d021fb17509045e67dea0e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart","hash":"56eb8fc294e50de35c4126fa70816306"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","hash":"56a61967670973048e56649e98abc752"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","hash":"e5022c82bb7c0c9e683421589dabf262"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","hash":"df3028881ead37c84a72186061f16e59"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"68c77aa51f2ba7ce1cc24a7ed22a648b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"ee2dfa2a8e23ad82d27e32d499083189"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"ebfcd08d4c861cc0c5d2d5371e430e56"},{"path":"/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"5ff2d90544438cc086555a06fe71334b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","hash":"b1264e09905a575bbb81f41fca7ed7d0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"f5e6b9da5f1fce53cb7a13ed0ac928d4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"72a20184b7c14aa3bebc1829168ec249"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"b8b1ab1d8d851e1efdaac119ca9aed51"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","hash":"d5b8c067b477d74d2fa41c295ea730c7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","hash":"003c28f62eb2fed28290fbf9063e0feb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","hash":"6a46248fad78bdec14b8ef3b35a8d702"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","hash":"bf9e6db2ca22d52bdfe962cf63ae83ec"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"a8421a85bad5a6adbff705117f683290"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","hash":"4d089aebcdb149b99acdf4104a781bf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"05da370179c31437bac685fae12ba5e3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"faab6eda68e6fc4782603410686f468a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"b344d11d93750b02904d1ee4b1e40d9e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"55ad5a67f7709ca095b64d46c68feaf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"125a748e9305bf2ff330e307b52ac99f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"5867a832ae87b5e70284d8987d79b4b3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","hash":"7dc1cfda766e41101fa0f954d83bf0f8"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"91fa72e09a0f06350440c87d81ae500c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"1783f60bcbf1764090536df245866c22"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","hash":"b14a4a2269f88c443a2785baad48a812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","hash":"3afb5256fb29b6388dd083e6427aa5dc"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","hash":"3ab484b13262b87bae729b710a69bd70"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"fc62522a963025f3b7fc2c6502083fe7"}]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json new file mode 100644 index 0000000000..d0a92c13db --- /dev/null +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json @@ -0,0 +1 @@ +{"inputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d new file mode 100644 index 0000000000..6d6112802c --- /dev/null +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d @@ -0,0 +1 @@ + /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart: /Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp new file mode 100644 index 0000000000..0f4a60ba1e --- /dev/null +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp @@ -0,0 +1 @@ +{"inputs":["/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json new file mode 100644 index 0000000000..e6da9694bf --- /dev/null +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json @@ -0,0 +1 @@ +["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"] \ No newline at end of file diff --git a/mobile/packages/strings/example/main.dart b/mobile/packages/strings/example/main.dart new file mode 100644 index 0000000000..e9533a11fa --- /dev/null +++ b/mobile/packages/strings/example/main.dart @@ -0,0 +1,90 @@ +import 'package:flutter/material.dart'; +import 'package:ente_strings/ente_strings.dart'; + +/// Example widget demonstrating how to use the ente_strings package +class ExampleStringUsage extends StatelessWidget { + const ExampleStringUsage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Ente Strings Example'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Network Error Message:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + // Example 1: Using the extension method + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.red[50], + border: Border.all(color: Colors.red[200]!), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + context.strings.networkHostLookUpErr, + style: const TextStyle(color: Colors.red), + ), + ), + const SizedBox(height: 16), + const Text( + 'Alternative usage:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 8), + // Example 2: Using the traditional approach + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: Colors.orange[50], + border: Border.all(color: Colors.orange[200]!), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + StringsLocalizations.of(context).networkHostLookUpErr, + style: const TextStyle(color: Colors.orange), + ), + ), + ], + ), + ), + ); + } +} + +/// Example main app demonstrating setup +class ExampleApp extends StatelessWidget { + const ExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Ente Strings Example', + // Configure localization delegates + localizationsDelegates: [ + ...StringsLocalizations.localizationsDelegates, + // Add your other app-specific delegates here + ], + supportedLocales: StringsLocalizations.supportedLocales, + home: const ExampleStringUsage(), + ); + } +} + +void main() { + runApp(const ExampleApp()); +} diff --git a/mobile/packages/strings/example/pubspec.lock b/mobile/packages/strings/example/pubspec.lock new file mode 100644 index 0000000000..bec753b599 --- /dev/null +++ b/mobile/packages/strings/example/pubspec.lock @@ -0,0 +1,225 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + ente_strings: + dependency: "direct main" + description: + path: ".." + relative: true + source: path + version: "1.0.0" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/strings/example/pubspec.yaml b/mobile/packages/strings/example/pubspec.yaml new file mode 100644 index 0000000000..4f369c23e0 --- /dev/null +++ b/mobile/packages/strings/example/pubspec.yaml @@ -0,0 +1,22 @@ +name: ente_strings_example +description: Example app demonstrating the ente_strings package +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + ente_strings: + path: ../ + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + uses-material-design: true diff --git a/mobile/packages/strings/l10n.yaml b/mobile/packages/strings/l10n.yaml new file mode 100644 index 0000000000..38319d1632 --- /dev/null +++ b/mobile/packages/strings/l10n.yaml @@ -0,0 +1,6 @@ +arb-dir: lib/l10n/arb +template-arb-file: strings_en.arb +output-localization-file: strings_localizations.dart +output-class: StringsLocalizations +output-dir: lib/l10n +nullable-getter: false diff --git a/mobile/packages/strings/lib/ente_strings.dart b/mobile/packages/strings/lib/ente_strings.dart new file mode 100644 index 0000000000..9cb83e454f --- /dev/null +++ b/mobile/packages/strings/lib/ente_strings.dart @@ -0,0 +1,5 @@ +/// A Flutter package containing shared localization strings for Ente apps +library ente_strings; + +export 'l10n/strings_localizations.dart'; +export 'extensions.dart'; diff --git a/mobile/packages/strings/lib/extensions.dart b/mobile/packages/strings/lib/extensions.dart new file mode 100644 index 0000000000..5aea9f908a --- /dev/null +++ b/mobile/packages/strings/lib/extensions.dart @@ -0,0 +1,11 @@ +import 'package:flutter/widgets.dart'; +import 'l10n/strings_localizations.dart'; + +// Re-export the localizations for convenience +export 'l10n/strings_localizations.dart'; + +/// Extension to easily access shared strings from any BuildContext +extension EnteStringsExtension on BuildContext { + /// Get the shared strings localizations for the current locale + StringsLocalizations get strings => StringsLocalizations.of(this); +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ar.arb b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb new file mode 100644 index 0000000000..a5580b6ce3 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb @@ -0,0 +1,9 @@ +{ + "networkHostLookUpErr": "تعذر الاتصال بـEnte، فضلا تحقق من إعدادات الشبكة الخاصة بك وتواصل مع الدعم إذا استمر الخطأ.", + "networkConnectionRefusedErr": "غير قادر على الاتصال بـ Ente، يرجى إعادة المحاولة بعد فترة. إذا استمر الخطأ، يرجى الاتصال بالدعم.", + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "يبدو أن خطأ ما حدث. يرجى إعادة المحاولة بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.", + "error": "خطأ", + "ok": "موافق", + "faq": "الأسئلة الشائعة", + "contactSupport": "اتصل بالدعم" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_bg.arb b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb new file mode 100644 index 0000000000..25b6e48826 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Не може да се свърже с Ente, моля, проверете мрежовите си настройки и се свържете с поддръжката, ако проблемът продължава." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_cs.arb b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb new file mode 100644 index 0000000000..ce0e3e368d --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Nelze se připojit k Ente, zkontrolujte, prosím, nastavení své sítě a kontaktujte podporu, pokud chyba přetrvává" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_da.arb b/mobile/packages/strings/lib/l10n/arb/strings_da.arb new file mode 100644 index 0000000000..e04451b45c --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_da.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Ude af stand til at forbinde til Ente. Tjek venligst dine netværksindstillinger og kontakt support hvis fejlen varer ved." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_el.arb b/mobile/packages/strings/lib/l10n/arb/strings_el.arb new file mode 100644 index 0000000000..001c0474de --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_el.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Δεν είναι δυνατή η σύνδεση με το Ente, ελέγξτε τις ρυθμίσεις του δικτύου σας και επικοινωνήστε με την υποστήριξη αν το σφάλμα παραμένει." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb new file mode 100644 index 0000000000..0e62bc571a --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -0,0 +1,110 @@ +{ + "networkHostLookUpErr": "Unable to connect to Ente, please check your network settings and contact support if the error persists.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Error", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contact support", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Email your logs", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Please send the logs to \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copy email address", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Export logs", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Cancel", + "@cancel": { + "description": "Cancel button label" + }, + "pleaseEmailUsAt": "Email us at {toEmail}", + "@pleaseEmailUsAt": { + "description": "Message showing email address for support", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Support email address" + } + } + }, + "emailAddressCopied": "Email address copied", + "@emailAddressCopied": { + "description": "Snackbar message when email address is copied" + }, + "supportEmailSubject": "[Support]", + "@supportEmailSubject": { + "description": "Default subject for support emails" + }, + "clientDebugInfoLabel": "Following information can help us in debugging if you are facing any issue", + "@clientDebugInfoLabel": { + "description": "Label for debug information in emails" + }, + "registeredEmailLabel": "Registered email:", + "@registeredEmailLabel": { + "description": "Label for registered email in debug info" + }, + "clientLabel": "Client:", + "@clientLabel": { + "description": "Label for client information in debug info" + }, + "versionLabel": "Version :", + "@versionLabel": { + "description": "Label for version information in debug info" + }, + "notAvailable": "N/A", + "@notAvailable": { + "description": "Not available text" + }, + "enteLogsPrefix": "ente-logs-", + "@enteLogsPrefix": { + "description": "Prefix for log file names" + }, + "logsDirectoryName": "logs", + "@logsDirectoryName": { + "description": "Name of logs directory" + }, + "logsZipFileName": "logs.zip", + "@logsZipFileName": { + "description": "Name of zipped log file" + }, + "zipFileExtension": "zip", + "@zipFileExtension": { + "description": "File extension for zip files" + } +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_es.arb b/mobile/packages/strings/lib/l10n/arb/strings_es.arb new file mode 100644 index 0000000000..ccfec794b3 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_es.arb @@ -0,0 +1,9 @@ +{ + "networkHostLookUpErr": "No se puede conectar a Ente, por favor verifica tu configuración de red y ponte en contacto con el soporte si el error persiste.", + "networkConnectionRefusedErr": "No se puede conectar a Ente. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con el soporte técnico.", + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Parece que algo salió mal. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con nuestro equipo de soporte.", + "error": "Error", + "ok": "Ok", + "faq": "Preguntas Frecuentes", + "contactSupport": "Ponerse en contacto con el equipo de soporte" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_fr.arb b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb new file mode 100644 index 0000000000..95e1d02e90 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb @@ -0,0 +1,9 @@ +{ + "networkHostLookUpErr": "Impossible de se connecter à Ente, veuillez vérifier vos paramètres réseau et contacter le support si l'erreur persiste.", + "networkConnectionRefusedErr": "Impossible de se connecter à Ente, veuillez réessayer après un certain temps. Si l'erreur persiste, veuillez contacter le support.", + "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", + "ok": "Ok", + "faq": "FAQ", + "contactSupport": "Contacter le support" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_id.arb b/mobile/packages/strings/lib/l10n/arb/strings_id.arb new file mode 100644 index 0000000000..d721d9f922 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_id.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Tidak dapat terhubung ke Ente. Mohon periksa kembali koneksi internet Anda dan hubungi tim bantuan kami jika galat masih ada." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ja.arb b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb new file mode 100644 index 0000000000..70fff21fe5 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb @@ -0,0 +1,9 @@ +{ + "networkHostLookUpErr": "Enteに接続できませんでした。ネットワーク設定を確認し、エラーが解決しない場合はサポートにお問い合わせください。", + "networkConnectionRefusedErr": "Enteに接続できません。しばらくしてから再試行してください。エラーが継続する場合は、サポートにお問い合わせください。", + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "問題が発生したようです。しばらくしてから再試行してください。エラーが継続する場合は、サポートチームにお問い合わせください。", + "error": "エラー", + "ok": "OK", + "faq": "FAQ", + "contactSupport": "サポートに連絡する" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ko.arb b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb new file mode 100644 index 0000000000..f71b8196d3 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Ente에 접속할 수 없습니다, 네트워크 설정을 확인해주시고 에러가 반복되는 경우 저희 지원 팀에 문의해주세요." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_lt.arb b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb new file mode 100644 index 0000000000..eca6c75216 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Nepavyksta prisijungti prie \"Ente\". Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_nl.arb b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb new file mode 100644 index 0000000000..f3cc33b846 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Kan geen verbinding maken met Ente, controleer uw netwerkinstellingen en neem contact op met ondersteuning als de fout zich blijft voordoen." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pl.arb b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb new file mode 100644 index 0000000000..bcd5668986 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Nie można połączyć się z Ente, sprawdź ustawienia sieci i skontaktuj się z pomocą techniczną, jeśli błąd będzie się powtarzał." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pt.arb b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb new file mode 100644 index 0000000000..e1e157fac5 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Não foi possível conectar-se ao Ente, verifique suas configurações de rede e entre em contato com o suporte se o erro persistir." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ru.arb b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb new file mode 100644 index 0000000000..9ee758ceda --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Не удается подключиться к Ente, пожалуйста, проверьте настройки своей сети и обратитесь в службу поддержки, если ошибка повторится." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sk.arb b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb new file mode 100644 index 0000000000..6f020d25be --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Nemožno sa pripojiť k Ente, skontrolujte svoje nastavenia siete a kontaktujte podporu, ak chyba pretrváva." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sr.arb b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb new file mode 100644 index 0000000000..a133eadd43 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Није могуће повезивање са Ente-ом, молимо вас да проверите мрежне поставке и контактирајте подршку ако грешка и даље постоји." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sv.arb b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb new file mode 100644 index 0000000000..8daede564e --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Det gick inte att ansluta till Ente, kontrollera dina nätverksinställningar och kontakta supporten om felet kvarstår." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_tr.arb b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb new file mode 100644 index 0000000000..2b7b873a60 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Ente'ye bağlanılamıyor, lütfen ağ ayarlarınızı kontrol edin ve hata devam ederse desteğe başvurun." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_vi.arb b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb new file mode 100644 index 0000000000..32f5e63fd4 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ." +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb new file mode 100644 index 0000000000..532b55dd09 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "无法连接到 Ente,请检查您的网络设置,如果错误仍然存在,请联系支持。" +} diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb new file mode 100644 index 0000000000..55b53dac78 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb @@ -0,0 +1,3 @@ +{ + "networkHostLookUpErr": "無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存在,請聯絡支援。" +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart new file mode 100644 index 0000000000..4df3a8a8e4 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -0,0 +1,394 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'strings_localizations_ar.dart'; +import 'strings_localizations_bg.dart'; +import 'strings_localizations_cs.dart'; +import 'strings_localizations_da.dart'; +import 'strings_localizations_el.dart'; +import 'strings_localizations_en.dart'; +import 'strings_localizations_es.dart'; +import 'strings_localizations_fr.dart'; +import 'strings_localizations_id.dart'; +import 'strings_localizations_ja.dart'; +import 'strings_localizations_ko.dart'; +import 'strings_localizations_lt.dart'; +import 'strings_localizations_nl.dart'; +import 'strings_localizations_pl.dart'; +import 'strings_localizations_pt.dart'; +import 'strings_localizations_ru.dart'; +import 'strings_localizations_sk.dart'; +import 'strings_localizations_sr.dart'; +import 'strings_localizations_sv.dart'; +import 'strings_localizations_tr.dart'; +import 'strings_localizations_vi.dart'; +import 'strings_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of StringsLocalizations +/// returned by `StringsLocalizations.of(context)`. +/// +/// Applications need to include `StringsLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/strings_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: StringsLocalizations.localizationsDelegates, +/// supportedLocales: StringsLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the StringsLocalizations.supportedLocales +/// property. +abstract class StringsLocalizations { + StringsLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static StringsLocalizations of(BuildContext context) { + return Localizations.of( + context, StringsLocalizations)!; + } + + static const LocalizationsDelegate delegate = + _StringsLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('ar'), + Locale('bg'), + Locale('cs'), + Locale('da'), + Locale('el'), + Locale('en'), + Locale('es'), + Locale('fr'), + Locale('id'), + Locale('ja'), + Locale('ko'), + Locale('lt'), + Locale('nl'), + Locale('pl'), + Locale('pt'), + Locale('ru'), + Locale('sk'), + Locale('sr'), + Locale('sv'), + Locale('tr'), + Locale('vi'), + Locale('zh'), + Locale('zh', 'TW') + ]; + + /// Error message shown when the app cannot connect to Ente due to network host lookup failure + /// + /// In en, this message translates to: + /// **'Unable to connect to Ente, please check your network settings and contact support if the error persists.'** + String get networkHostLookUpErr; + + /// Error message shown when the app cannot connect to Ente due to connection refused + /// + /// In en, this message translates to: + /// **'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'** + String get networkConnectionRefusedErr; + + /// Generic error message for temporary issues + /// + /// In en, this message translates to: + /// **'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'** + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome; + + /// Generic error title + /// + /// In en, this message translates to: + /// **'Error'** + String get error; + + /// Generic OK button label + /// + /// In en, this message translates to: + /// **'Ok'** + String get ok; + + /// FAQ link label + /// + /// In en, this message translates to: + /// **'FAQ'** + String get faq; + + /// Contact support button label + /// + /// In en, this message translates to: + /// **'Contact support'** + String get contactSupport; + + /// Title for emailing logs dialog + /// + /// In en, this message translates to: + /// **'Email your logs'** + String get emailYourLogs; + + /// Message asking user to send logs to email address + /// + /// In en, this message translates to: + /// **'Please send the logs to \n{toEmail}'** + String pleaseSendTheLogsTo(String toEmail); + + /// Button to copy email address to clipboard + /// + /// In en, this message translates to: + /// **'Copy email address'** + String get copyEmailAddress; + + /// Button to export logs + /// + /// In en, this message translates to: + /// **'Export logs'** + String get exportLogs; + + /// Cancel button label + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// Message showing email address for support + /// + /// In en, this message translates to: + /// **'Email us at {toEmail}'** + String pleaseEmailUsAt(String toEmail); + + /// Snackbar message when email address is copied + /// + /// In en, this message translates to: + /// **'Email address copied'** + String get emailAddressCopied; + + /// Default subject for support emails + /// + /// In en, this message translates to: + /// **'[Support]'** + String get supportEmailSubject; + + /// Label for debug information in emails + /// + /// In en, this message translates to: + /// **'Following information can help us in debugging if you are facing any issue'** + String get clientDebugInfoLabel; + + /// Label for registered email in debug info + /// + /// In en, this message translates to: + /// **'Registered email:'** + String get registeredEmailLabel; + + /// Label for client information in debug info + /// + /// In en, this message translates to: + /// **'Client:'** + String get clientLabel; + + /// Label for version information in debug info + /// + /// In en, this message translates to: + /// **'Version :'** + String get versionLabel; + + /// Not available text + /// + /// In en, this message translates to: + /// **'N/A'** + String get notAvailable; + + /// Prefix for log file names + /// + /// In en, this message translates to: + /// **'ente-logs-'** + String get enteLogsPrefix; + + /// Name of logs directory + /// + /// In en, this message translates to: + /// **'logs'** + String get logsDirectoryName; + + /// Name of zipped log file + /// + /// In en, this message translates to: + /// **'logs.zip'** + String get logsZipFileName; + + /// File extension for zip files + /// + /// In en, this message translates to: + /// **'zip'** + String get zipFileExtension; +} + +class _StringsLocalizationsDelegate + extends LocalizationsDelegate { + const _StringsLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture( + lookupStringsLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => [ + 'ar', + 'bg', + 'cs', + 'da', + 'el', + 'en', + 'es', + 'fr', + 'id', + 'ja', + 'ko', + 'lt', + 'nl', + 'pl', + 'pt', + 'ru', + 'sk', + 'sr', + 'sv', + 'tr', + 'vi', + 'zh' + ].contains(locale.languageCode); + + @override + bool shouldReload(_StringsLocalizationsDelegate old) => false; +} + +StringsLocalizations lookupStringsLocalizations(Locale locale) { + // Lookup logic when language+country codes are specified. + switch (locale.languageCode) { + case 'zh': + { + switch (locale.countryCode) { + case 'TW': + return StringsLocalizationsZhTw(); + } + break; + } + } + + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'ar': + return StringsLocalizationsAr(); + case 'bg': + return StringsLocalizationsBg(); + case 'cs': + return StringsLocalizationsCs(); + case 'da': + return StringsLocalizationsDa(); + case 'el': + return StringsLocalizationsEl(); + case 'en': + return StringsLocalizationsEn(); + case 'es': + return StringsLocalizationsEs(); + case 'fr': + return StringsLocalizationsFr(); + case 'id': + return StringsLocalizationsId(); + case 'ja': + return StringsLocalizationsJa(); + case 'ko': + return StringsLocalizationsKo(); + case 'lt': + return StringsLocalizationsLt(); + case 'nl': + return StringsLocalizationsNl(); + case 'pl': + return StringsLocalizationsPl(); + case 'pt': + return StringsLocalizationsPt(); + case 'ru': + return StringsLocalizationsRu(); + case 'sk': + return StringsLocalizationsSk(); + case 'sr': + return StringsLocalizationsSr(); + case 'sv': + return StringsLocalizationsSv(); + case 'tr': + return StringsLocalizationsTr(); + case 'vi': + return StringsLocalizationsVi(); + case 'zh': + return StringsLocalizationsZh(); + } + + throw FlutterError( + 'StringsLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart new file mode 100644 index 0000000000..113782aa8c --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Arabic (`ar`). +class StringsLocalizationsAr extends StringsLocalizations { + StringsLocalizationsAr([String locale = 'ar']) : super(locale); + + @override + String get networkHostLookUpErr => + 'تعذر الاتصال بـEnte، فضلا تحقق من إعدادات الشبكة الخاصة بك وتواصل مع الدعم إذا استمر الخطأ.'; + + @override + String get networkConnectionRefusedErr => + 'غير قادر على الاتصال بـ Ente، يرجى إعادة المحاولة بعد فترة. إذا استمر الخطأ، يرجى الاتصال بالدعم.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'يبدو أن خطأ ما حدث. يرجى إعادة المحاولة بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.'; + + @override + String get error => 'خطأ'; + + @override + String get ok => 'موافق'; + + @override + String get faq => 'الأسئلة الشائعة'; + + @override + String get contactSupport => 'اتصل بالدعم'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart new file mode 100644 index 0000000000..7206ac8c5e --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Bulgarian (`bg`). +class StringsLocalizationsBg extends StringsLocalizations { + StringsLocalizationsBg([String locale = 'bg']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Не може да се свърже с Ente, моля, проверете мрежовите си настройки и се свържете с поддръжката, ако проблемът продължава.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart new file mode 100644 index 0000000000..becd03374d --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Czech (`cs`). +class StringsLocalizationsCs extends StringsLocalizations { + StringsLocalizationsCs([String locale = 'cs']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Nelze se připojit k Ente, zkontrolujte, prosím, nastavení své sítě a kontaktujte podporu, pokud chyba přetrvává'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart new file mode 100644 index 0000000000..be2d30466a --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Danish (`da`). +class StringsLocalizationsDa extends StringsLocalizations { + StringsLocalizationsDa([String locale = 'da']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Ude af stand til at forbinde til Ente. Tjek venligst dine netværksindstillinger og kontakt support hvis fejlen varer ved.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart new file mode 100644 index 0000000000..c6de3a57f5 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Modern Greek (`el`). +class StringsLocalizationsEl extends StringsLocalizations { + StringsLocalizationsEl([String locale = 'el']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Δεν είναι δυνατή η σύνδεση με το Ente, ελέγξτε τις ρυθμίσεις του δικτύου σας και επικοινωνήστε με την υποστήριξη αν το σφάλμα παραμένει.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart new file mode 100644 index 0000000000..d6c7f2392f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class StringsLocalizationsEn extends StringsLocalizations { + StringsLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart new file mode 100644 index 0000000000..6bfb3b4518 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Spanish Castilian (`es`). +class StringsLocalizationsEs extends StringsLocalizations { + StringsLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get networkHostLookUpErr => + 'No se puede conectar a Ente, por favor verifica tu configuración de red y ponte en contacto con el soporte si el error persiste.'; + + @override + String get networkConnectionRefusedErr => + 'No se puede conectar a Ente. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con el soporte técnico.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Parece que algo salió mal. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con nuestro equipo de soporte.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'Preguntas Frecuentes'; + + @override + String get contactSupport => 'Ponerse en contacto con el equipo de soporte'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart new file mode 100644 index 0000000000..04c742b720 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for French (`fr`). +class StringsLocalizationsFr extends StringsLocalizations { + StringsLocalizationsFr([String locale = 'fr']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Impossible de se connecter à Ente, veuillez vérifier vos paramètres réseau et contacter le support si l\'erreur persiste.'; + + @override + String get networkConnectionRefusedErr => + 'Impossible de se connecter à Ente, veuillez réessayer après un certain temps. Si l\'erreur persiste, veuillez contacter le support.'; + + @override + String get 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.'; + + @override + String get error => 'Erreur'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contacter le support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart new file mode 100644 index 0000000000..14653f5e3c --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Indonesian (`id`). +class StringsLocalizationsId extends StringsLocalizations { + StringsLocalizationsId([String locale = 'id']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Tidak dapat terhubung ke Ente. Mohon periksa kembali koneksi internet Anda dan hubungi tim bantuan kami jika galat masih ada.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart new file mode 100644 index 0000000000..7f09791dac --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Japanese (`ja`). +class StringsLocalizationsJa extends StringsLocalizations { + StringsLocalizationsJa([String locale = 'ja']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Enteに接続できませんでした。ネットワーク設定を確認し、エラーが解決しない場合はサポートにお問い合わせください。'; + + @override + String get networkConnectionRefusedErr => + 'Enteに接続できません。しばらくしてから再試行してください。エラーが継続する場合は、サポートにお問い合わせください。'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + '問題が発生したようです。しばらくしてから再試行してください。エラーが継続する場合は、サポートチームにお問い合わせください。'; + + @override + String get error => 'エラー'; + + @override + String get ok => 'OK'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'サポートに連絡する'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart new file mode 100644 index 0000000000..7b593fb9c2 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Korean (`ko`). +class StringsLocalizationsKo extends StringsLocalizations { + StringsLocalizationsKo([String locale = 'ko']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Ente에 접속할 수 없습니다, 네트워크 설정을 확인해주시고 에러가 반복되는 경우 저희 지원 팀에 문의해주세요.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart new file mode 100644 index 0000000000..d094cc0f79 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Lithuanian (`lt`). +class StringsLocalizationsLt extends StringsLocalizations { + StringsLocalizationsLt([String locale = 'lt']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Nepavyksta prisijungti prie \"Ente\". Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart new file mode 100644 index 0000000000..55d5b918e8 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Dutch Flemish (`nl`). +class StringsLocalizationsNl extends StringsLocalizations { + StringsLocalizationsNl([String locale = 'nl']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Kan geen verbinding maken met Ente, controleer uw netwerkinstellingen en neem contact op met ondersteuning als de fout zich blijft voordoen.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart new file mode 100644 index 0000000000..a38df6da4f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Polish (`pl`). +class StringsLocalizationsPl extends StringsLocalizations { + StringsLocalizationsPl([String locale = 'pl']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Nie można połączyć się z Ente, sprawdź ustawienia sieci i skontaktuj się z pomocą techniczną, jeśli błąd będzie się powtarzał.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart new file mode 100644 index 0000000000..ace7c84912 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Portuguese (`pt`). +class StringsLocalizationsPt extends StringsLocalizations { + StringsLocalizationsPt([String locale = 'pt']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Não foi possível conectar-se ao Ente, verifique suas configurações de rede e entre em contato com o suporte se o erro persistir.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart new file mode 100644 index 0000000000..f78e6dd1b7 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Russian (`ru`). +class StringsLocalizationsRu extends StringsLocalizations { + StringsLocalizationsRu([String locale = 'ru']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Не удается подключиться к Ente, пожалуйста, проверьте настройки своей сети и обратитесь в службу поддержки, если ошибка повторится.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart new file mode 100644 index 0000000000..0275fc51fd --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Slovak (`sk`). +class StringsLocalizationsSk extends StringsLocalizations { + StringsLocalizationsSk([String locale = 'sk']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Nemožno sa pripojiť k Ente, skontrolujte svoje nastavenia siete a kontaktujte podporu, ak chyba pretrváva.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart new file mode 100644 index 0000000000..84464d5c8f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Serbian (`sr`). +class StringsLocalizationsSr extends StringsLocalizations { + StringsLocalizationsSr([String locale = 'sr']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Није могуће повезивање са Ente-ом, молимо вас да проверите мрежне поставке и контактирајте подршку ако грешка и даље постоји.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart new file mode 100644 index 0000000000..7eb4f6e327 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Swedish (`sv`). +class StringsLocalizationsSv extends StringsLocalizations { + StringsLocalizationsSv([String locale = 'sv']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Det gick inte att ansluta till Ente, kontrollera dina nätverksinställningar och kontakta supporten om felet kvarstår.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart new file mode 100644 index 0000000000..9c8cb203c8 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Turkish (`tr`). +class StringsLocalizationsTr extends StringsLocalizations { + StringsLocalizationsTr([String locale = 'tr']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Ente\'ye bağlanılamıyor, lütfen ağ ayarlarınızı kontrol edin ve hata devam ederse desteğe başvurun.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart new file mode 100644 index 0000000000..7479467a50 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -0,0 +1,90 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Vietnamese (`vi`). +class StringsLocalizationsVi extends StringsLocalizations { + StringsLocalizationsVi([String locale = 'vi']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart new file mode 100644 index 0000000000..6d5db16f30 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -0,0 +1,97 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class StringsLocalizationsZh extends StringsLocalizations { + StringsLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存在,请联系支持。'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get enteLogsPrefix => 'ente-logs-'; + + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; +} + +/// The translations for Chinese, as used in Taiwan (`zh_TW`). +class StringsLocalizationsZhTw extends StringsLocalizationsZh { + StringsLocalizationsZhTw() : super('zh_TW'); + + @override + String get networkHostLookUpErr => '無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存在,請聯絡支援。'; +} diff --git a/mobile/packages/strings/pubspec.lock b/mobile/packages/strings/pubspec.lock new file mode 100644 index 0000000000..97843e33f0 --- /dev/null +++ b/mobile/packages/strings/pubspec.lock @@ -0,0 +1,218 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" +sdks: + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/strings/pubspec.yaml b/mobile/packages/strings/pubspec.yaml new file mode 100644 index 0000000000..e372063809 --- /dev/null +++ b/mobile/packages/strings/pubspec.yaml @@ -0,0 +1,22 @@ +name: ente_strings +description: A Flutter package containing shared localization strings for ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + flutter_localizations: + sdk: flutter + intl: any + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + generate: true diff --git a/mobile/packages/strings/test/strings_test.dart b/mobile/packages/strings/test/strings_test.dart new file mode 100644 index 0000000000..4e58c72a4e --- /dev/null +++ b/mobile/packages/strings/test/strings_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/widgets.dart'; +import 'package:ente_strings/ente_strings.dart'; + +void main() { + group('StringsLocalizations', () { + test('should include English locale', () { + expect( + StringsLocalizations.supportedLocales, + contains(const Locale('en')), + ); + }); + + test('should include multiple locales', () { + expect(StringsLocalizations.supportedLocales.length, greaterThan(10)); + + // Check for some key languages + expect( + StringsLocalizations.supportedLocales, + contains(const Locale('fr')), + ); + expect( + StringsLocalizations.supportedLocales, + contains(const Locale('ja')), + ); + expect( + StringsLocalizations.supportedLocales, + contains(const Locale('zh')), + ); + }); + }); +} From 42e6dff0f5c05f811bc714a2f41b709581b99bd9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 19:45:55 +0530 Subject: [PATCH 015/164] Remove redundant test --- .../packages/strings/test/strings_test.dart | 32 ------------------- 1 file changed, 32 deletions(-) delete mode 100644 mobile/packages/strings/test/strings_test.dart diff --git a/mobile/packages/strings/test/strings_test.dart b/mobile/packages/strings/test/strings_test.dart deleted file mode 100644 index 4e58c72a4e..0000000000 --- a/mobile/packages/strings/test/strings_test.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/widgets.dart'; -import 'package:ente_strings/ente_strings.dart'; - -void main() { - group('StringsLocalizations', () { - test('should include English locale', () { - expect( - StringsLocalizations.supportedLocales, - contains(const Locale('en')), - ); - }); - - test('should include multiple locales', () { - expect(StringsLocalizations.supportedLocales.length, greaterThan(10)); - - // Check for some key languages - expect( - StringsLocalizations.supportedLocales, - contains(const Locale('fr')), - ); - expect( - StringsLocalizations.supportedLocales, - contains(const Locale('ja')), - ); - expect( - StringsLocalizations.supportedLocales, - contains(const Locale('zh')), - ); - }); - }); -} From 995ae50418b4126e60395e5fafa4cdf0b1344510 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 19:46:21 +0530 Subject: [PATCH 016/164] Refactor common utils --- .../packages/utils/lib/directory_utils.dart | 32 + mobile/packages/utils/lib/email_util.dart | 244 +++++ mobile/packages/utils/lib/ente_utils.dart | 6 + mobile/packages/utils/lib/platform_util.dart | 70 ++ mobile/packages/utils/lib/share_utils.dart | 71 ++ mobile/packages/utils/pubspec.lock | 994 ++++++++++++++++++ mobile/packages/utils/pubspec.yaml | 42 + 7 files changed, 1459 insertions(+) create mode 100644 mobile/packages/utils/lib/directory_utils.dart create mode 100644 mobile/packages/utils/lib/email_util.dart create mode 100644 mobile/packages/utils/lib/ente_utils.dart create mode 100644 mobile/packages/utils/lib/platform_util.dart create mode 100644 mobile/packages/utils/lib/share_utils.dart create mode 100644 mobile/packages/utils/pubspec.lock create mode 100644 mobile/packages/utils/pubspec.yaml diff --git a/mobile/packages/utils/lib/directory_utils.dart b/mobile/packages/utils/lib/directory_utils.dart new file mode 100644 index 0000000000..7fdbc6de67 --- /dev/null +++ b/mobile/packages/utils/lib/directory_utils.dart @@ -0,0 +1,32 @@ +import 'dart:io'; + +import 'package:ente_logging/logging.dart'; +import 'package:path/path.dart' as p; +import 'package:path_provider/path_provider.dart'; + +class DirectoryUtils { + static final logger = Logger('DirectoryUtils'); + + static Future getDatabasePath(String databaseName) async { + String? directoryPath; + + directoryPath ??= (await getApplicationSupportDirectory()).path; + + return p.joinAll( + [ + directoryPath, + ".$databaseName", + ], + ); + } + + static Future getDirectoryForInit() async { + Directory directory = await getApplicationCacheDirectory(); + + return Directory(p.join(directory.path, "init")); + } + + static Future getTempsDir() async { + return await getTemporaryDirectory(); + } +} diff --git a/mobile/packages/utils/lib/email_util.dart b/mobile/packages/utils/lib/email_util.dart new file mode 100644 index 0000000000..1f98a20b72 --- /dev/null +++ b/mobile/packages/utils/lib/email_util.dart @@ -0,0 +1,244 @@ +import 'dart:io'; + +import 'package:archive/archive_io.dart'; +import 'package:email_validator/email_validator.dart'; +import 'package:ente_logging/logging.dart'; +import 'package:ente_strings/extensions.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/dialog_widget.dart'; +import 'package:flutter_email_sender/flutter_email_sender.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_utils/directory_utils.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:ente_utils/share_utils.dart'; +import "package:file_saver/file_saver.dart"; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import "package:intl/intl.dart"; +import 'package:logging/logging.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:url_launcher/url_launcher.dart'; + +final Logger _logger = Logger('email_util'); + +bool isValidEmail(String? email) { + if (email == null) { + return false; + } + return EmailValidator.validate(email); +} + +Future sendLogs( + BuildContext context, + String toEmail, + String? subject, + String? body, +) async { + final String zipFilePath = await getZippedLogsFile(); + final Email email = Email( + recipients: [toEmail], + subject: subject ?? '', + body: body ?? '', + attachmentPaths: [zipFilePath], + isHTML: false, + ); + try { + await FlutterEmailSender.send(email); + } catch (e, s) { + _logger.severe('email sender failed', e, s); + Navigator.of(context).pop(); + await shareLogs(context, toEmail, zipFilePath); + } +} + +Future shareLogs( + BuildContext context, + String toEmail, + String zipFilePath, +) async { + final result = await showDialogWidget( + context: context, + title: context.strings.emailYourLogs, + body: context.strings.pleaseSendTheLogsTo(toEmail), + buttons: [ + ButtonWidget( + buttonType: ButtonType.neutral, + labelText: context.strings.copyEmailAddress, + isInAlert: true, + buttonAction: ButtonAction.first, + onTap: () async { + await Clipboard.setData(ClipboardData(text: toEmail)); + }, + shouldShowSuccessConfirmation: true, + ), + ButtonWidget( + buttonType: ButtonType.neutral, + labelText: context.strings.exportLogs, + isInAlert: true, + buttonAction: ButtonAction.second, + ), + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.cancel, + isInAlert: true, + buttonAction: ButtonAction.cancel, + ), + ], + ); + if (result?.action != null && result!.action == ButtonAction.second) { + await exportLogs(context, zipFilePath); + } +} + +Future openSupportPage( + String? subject, + String? body, +) async { + const url = "https://github.com/ente-io/ente/discussions/new?category=q-a"; + if (subject != null && body != null) { + await launchUrl( + Uri.parse( + "$url&title=$subject&body=$body", + ), + ); + } else { + await launchUrl(Uri.parse(url)); + } +} + +Future getZippedLogsFile({ + String logsSubPath = "logs", +}) async { + final logsPath = (await getApplicationSupportDirectory()).path; + final logsDirectory = Directory("$logsPath/$logsSubPath"); + final tempPath = (await DirectoryUtils.getTempsDir()).path; + final zipFilePath = "$tempPath/logs.zip"; + final encoder = ZipFileEncoder(); + encoder.create(zipFilePath); + await encoder.addDirectory(logsDirectory); + await encoder.close(); + return zipFilePath; +} + +Future exportLogs( + BuildContext context, + String zipFilePath, [ + bool isSharing = false, +]) async { + if (!isSharing) { + final DateTime now = DateTime.now().toUtc(); + final String shortMonthName = DateFormat('MMM').format(now); // Short month + final String logFileName = + 'ente-logs-${now.year}-$shortMonthName-${now.day}-${now.hour}-${now.minute}'; + + final bytes = await File(zipFilePath).readAsBytes(); + await PlatformUtil.shareFile( + logFileName, + 'zip', + bytes, + MimeType.zip, + ); + } else { + await shareFiles( + [XFile(zipFilePath, mimeType: 'application/zip')], + context: context, + ); + } +} + +Future sendLogsViaEmail( + String toEmail, + String? subject, + String? body, +) async { + final String zipFilePath = await getZippedLogsFile(); + final Email email = Email( + recipients: [toEmail], + subject: subject ?? '', + body: body ?? '', + attachmentPaths: [zipFilePath], + isHTML: false, + ); + try { + await FlutterEmailSender.send(email); + } catch (e, s) { + _logger.severe('email sender failed', e, s); + rethrow; + } +} + +Future sendEmail( + BuildContext context, { + required String to, + String? subject, + String? body, + BaseConfiguration? configuration, +}) async { + try { + final String clientDebugInfo = await _clientInfo(configuration); + final String subject0 = subject ?? '[Support]'; + final String body0 = (body ?? '') + clientDebugInfo; + + if (Platform.isAndroid) { + // Special handling due to issue in proton mail android client + // https://github.com/ente-io/frame/pull/253 + final Uri params = Uri( + scheme: 'mailto', + path: to, + query: 'subject=$subject0&body=$body0', + ); + if (await canLaunchUrl(params)) { + await launchUrl(params); + } else { + // this will trigger _showNoMailAppsDialog + throw Exception('Could not launch ${params.toString()}'); + } + } else { + _showNoMailAppsDialog(context, to); + } + } catch (e) { + _logger.severe("Failed to send email to $to", e); + _showNoMailAppsDialog(context, to); + } +} + +Future _clientInfo(BaseConfiguration? configuration) async { + final packageInfo = await PackageInfo.fromPlatform(); + final String debugInfo = + '\n\n\n\n ------------------- \nFollowing information can ' + 'help us in debugging if you are facing any issue ' + '\nRegistered email: ${configuration?.getEmail() ?? 'N/A'}' + '\nClient: ${packageInfo.packageName}' + '\nVersion : ${packageInfo.version}'; + return debugInfo; +} + +void _showNoMailAppsDialog(BuildContext context, String toEmail) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.email_outlined), + title: Text('Email us at $toEmail'), + actions: [ + TextButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: toEmail)); + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar(content: Text('Email address copied')), + ); + }, + child: const Text('Copy Email Address'), + ), + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('OK'), + ), + ], + ); + }, + ); +} diff --git a/mobile/packages/utils/lib/ente_utils.dart b/mobile/packages/utils/lib/ente_utils.dart new file mode 100644 index 0000000000..b431dc7ff9 --- /dev/null +++ b/mobile/packages/utils/lib/ente_utils.dart @@ -0,0 +1,6 @@ +library ente_utils; + +export 'directory_utils.dart'; +export 'email_util.dart'; +export 'platform_util.dart'; +export 'share_utils.dart'; diff --git a/mobile/packages/utils/lib/platform_util.dart b/mobile/packages/utils/lib/platform_util.dart new file mode 100644 index 0000000000..e84349c301 --- /dev/null +++ b/mobile/packages/utils/lib/platform_util.dart @@ -0,0 +1,70 @@ +import 'dart:io'; + +import 'package:file_saver/file_saver.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher_string.dart'; +import 'package:window_manager/window_manager.dart'; + +class PlatformUtil { + static bool isDesktop() { + return !kIsWeb && + (Platform.isWindows || Platform.isLinux || Platform.isMacOS); + } + + static bool isMobile() { + return !kIsWeb && (Platform.isAndroid || Platform.isIOS); + } + + static bool isWeb() { + return kIsWeb; + } + + static TextSelectionControls get selectionControls => Platform.isAndroid + ? materialTextSelectionControls + : Platform.isIOS + ? cupertinoTextSelectionControls + : desktopTextSelectionControls; + + static openWebView(BuildContext context, String title, String url) async { + // For desktop, always open in external browser + // For mobile, open in external browser (apps can override this if they have web view) + await launchUrlString(url); + } + + static Future shareFile( + String fileName, + String extension, + Uint8List bytes, + MimeType type, + ) async { + try { + if (Platform.isAndroid || Platform.isIOS) { + await FileSaver.instance.saveAs( + name: fileName, + ext: extension, + bytes: bytes, + mimeType: type, + ); + } else { + await FileSaver.instance.saveFile( + name: fileName, + ext: extension, + bytes: bytes, + mimeType: type, + ); + } + } catch (_) {} + } + + // Needed to fix issue with local_auth on Windows + // https://github.com/flutter/flutter/issues/122322 + static Future refocusWindows() async { + if (!Platform.isWindows) return; + await windowManager.blur(); + await windowManager.focus(); + await windowManager.setAlwaysOnTop(true); + await windowManager.setAlwaysOnTop(false); + } +} diff --git a/mobile/packages/utils/lib/share_utils.dart b/mobile/packages/utils/lib/share_utils.dart new file mode 100644 index 0000000000..c7cf0e8de5 --- /dev/null +++ b/mobile/packages/utils/lib/share_utils.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:share_plus/share_plus.dart'; + +Rect _sharePosOrigin(BuildContext? context, GlobalKey? key) { + late final Rect rect; + if (context != null) { + rect = shareButtonRect(context, key); + } else { + rect = const Offset(20.0, 20.0) & const Size(10, 10); + } + return rect; +} + +/// Returns the rect of button if context and key are not null +/// If key is null, returned rect will be at the center of the screen +Rect shareButtonRect(BuildContext context, GlobalKey? shareButtonKey) { + Size size = MediaQuery.sizeOf(context); + final RenderObject? renderObject = + shareButtonKey?.currentContext?.findRenderObject(); + RenderBox? renderBox; + if (renderObject != null && renderObject is RenderBox) { + renderBox = renderObject; + } + if (renderBox == null) { + return Rect.fromLTWH(0, 0, size.width, size.height / 2); + } + size = renderBox.size; + final Offset position = renderBox.localToGlobal(Offset.zero); + return Rect.fromCenter( + center: position + Offset(size.width / 2, size.height / 2), + width: size.width, + height: size.height, + ); +} + +Future shareText( + String text, { + BuildContext? context, + GlobalKey? key, +}) async { + try { + final sharePosOrigin = _sharePosOrigin(context, key); + return Share.share( + text, + sharePositionOrigin: sharePosOrigin, + ); + } catch (e, s) { + Logger("ShareUtil").severe("failed to share text", e, s); + return ShareResult.unavailable; + } +} + +Future shareFiles( + List files, { + BuildContext? context, + GlobalKey? key, + String? text, +}) async { + try { + final sharePosOrigin = _sharePosOrigin(context, key); + return Share.shareXFiles( + files, + text: text, + sharePositionOrigin: sharePosOrigin, + ); + } catch (e, s) { + Logger("ShareUtil").severe("failed to share files", e, s); + return ShareResult.unavailable; + } +} diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock new file mode 100644 index 0000000000..9b33654cf8 --- /dev/null +++ b/mobile/packages/utils/pubspec.lock @@ -0,0 +1,994 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: "direct main" + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: transitive + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: transitive + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + email_validator: + dependency: "direct main" + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_base: + dependency: transitive + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: transitive + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: "direct main" + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: "direct main" + description: + path: "../ui" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_saver: + dependency: "direct main" + description: + name: file_saver + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" + url: "https://pub.dev" + source: hosted + version: "0.2.14" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_email_sender: + dependency: "direct main" + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + path: + dependency: "direct main" + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.dev" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml new file mode 100644 index 0000000000..648b0f0ac6 --- /dev/null +++ b/mobile/packages/utils/pubspec.yaml @@ -0,0 +1,42 @@ +name: ente_utils +description: A Flutter package containing shared utility functions for Ente apps +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + # Core dependencies + archive: ^4.0.7 + email_validator: ^3.0.0 + file_saver: ^0.2.14 + flutter_email_sender: ^7.0.0 + ente_logging: + path: ../../packages/logging + ente_strings: + path: ../../packages/strings + ente_ui: + path: ../../packages/ui + intl: ^0.20.1 + logging: ^1.3.0 + package_info_plus: ^8.1.1 + path: ^1.9.1 + path_provider: ^2.1.5 + share_plus: ^10.1.2 + url_launcher: ^6.3.1 + window_manager: ^0.5.0 + + # Ente packages + ente_configuration: + path: ../../packages/configuration + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From 93736fe57af8b3bc9d939cdc4c81efe695cf901f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 19:56:03 +0530 Subject: [PATCH 017/164] Update common strings --- .../strings/lib/l10n/arb/strings_en.arb | 12 ++++++++++++ .../lib/l10n/strings_localizations.dart | 18 ++++++++++++++++++ .../lib/l10n/strings_localizations_ar.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_bg.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_cs.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_da.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_el.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_en.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_es.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_fr.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_id.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_ja.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_ko.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_lt.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_nl.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_pl.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_pt.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_ru.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_sk.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_sr.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_sv.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_tr.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_vi.dart | 10 ++++++++++ .../lib/l10n/strings_localizations_zh.dart | 10 ++++++++++ 24 files changed, 250 insertions(+) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 0e62bc571a..56bdf32690 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -106,5 +106,17 @@ "zipFileExtension": "zip", "@zipFileExtension": { "description": "File extension for zip files" + }, + "reportABug": "Report a bug", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "logsDialogBody": "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.", + "@logsDialogBody": { + "description": "Body text for the logs dialog explaining what will be sent" + }, + "viewLogs": "View logs", + "@viewLogs": { + "description": "Button to view logs" } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index 4df3a8a8e4..ea43fc9a2d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -283,6 +283,24 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'zip'** String get zipFileExtension; + + /// Label for reporting a bug + /// + /// In en, this message translates to: + /// **'Report a bug'** + String get reportABug; + + /// Body text for the logs dialog explaining what will be sent + /// + /// In en, this message translates to: + /// **'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; + + /// Button to view logs + /// + /// In en, this message translates to: + /// **'View logs'** + String get viewLogs; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 113782aa8c..39f79d3e4f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -87,4 +87,14 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 7206ac8c5e..1987d786e1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -87,4 +87,14 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index becd03374d..9f37112311 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -87,4 +87,14 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index be2d30466a..5458a85d41 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -87,4 +87,14 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index c6de3a57f5..45d4bcacc8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -87,4 +87,14 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index d6c7f2392f..c09af89599 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -87,4 +87,14 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 6bfb3b4518..0861a80978 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -87,4 +87,14 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 04c742b720..e99292c174 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -87,4 +87,14 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 14653f5e3c..ea282f632e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -87,4 +87,14 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 7f09791dac..c13bfb5ef2 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -87,4 +87,14 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index 7b593fb9c2..ef135b61d4 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -87,4 +87,14 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index d094cc0f79..7b9ba7eb34 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -87,4 +87,14 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 55d5b918e8..220cdd4f55 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -87,4 +87,14 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index a38df6da4f..9b2d7f5cbf 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -87,4 +87,14 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index ace7c84912..1960289a34 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -87,4 +87,14 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index f78e6dd1b7..c326fbce72 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -87,4 +87,14 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 0275fc51fd..85bed4aa92 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -87,4 +87,14 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 84464d5c8f..d909c41cc0 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -87,4 +87,14 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 7eb4f6e327..2da70572f9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -87,4 +87,14 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index 9c8cb203c8..81a3b52801 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -87,4 +87,14 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 7479467a50..983a5f28a9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -87,4 +87,14 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 6d5db16f30..7cf2e36d58 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -86,6 +86,16 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From 00b05e2d7c8942a84cfa41b9e44f6dea1047a78a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 19:56:53 +0530 Subject: [PATCH 018/164] Update common utils --- mobile/packages/utils/lib/email_util.dart | 67 ++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/mobile/packages/utils/lib/email_util.dart b/mobile/packages/utils/lib/email_util.dart index 1f98a20b72..21ccada2f8 100644 --- a/mobile/packages/utils/lib/email_util.dart +++ b/mobile/packages/utils/lib/email_util.dart @@ -7,6 +7,7 @@ import 'package:ente_strings/extensions.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/components/dialog_widget.dart'; +import 'package:ente_ui/pages/log_file_viewer.dart'; import 'package:flutter_email_sender/flutter_email_sender.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_utils/directory_utils.dart'; @@ -16,7 +17,6 @@ import "package:file_saver/file_saver.dart"; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import "package:intl/intl.dart"; -import 'package:logging/logging.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; @@ -32,6 +32,71 @@ bool isValidEmail(String? email) { } Future sendLogs( + BuildContext context, + String toEmail, { + Function? postShare, + String? subject, + String? body, +}) async { + // ignore: unawaited_futures + showDialogWidget( + context: context, + title: context.strings.reportABug, + icon: Icons.bug_report_outlined, + body: context.strings.logsDialogBody, + buttons: [ + ButtonWidget( + isInAlert: true, + buttonType: ButtonType.neutral, + labelText: context.strings.reportABug, + buttonAction: ButtonAction.first, + shouldSurfaceExecutionStates: false, + onTap: () async { + await _sendLogs(context, toEmail, subject, body); + if (postShare != null) { + postShare(); + } + }, + ), + //isInAlert is false here as we don't want to the dialog to dismiss + //on pressing this button + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.viewLogs, + buttonAction: ButtonAction.second, + onTap: () async { + // ignore: unawaited_futures + showDialog( + useRootNavigator: false, + context: context, + builder: (BuildContext context) { + return LogFileViewer(SuperLogging.logFile!); + }, + barrierColor: Colors.black87, + barrierDismissible: false, + ); + }, + ), + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.exportLogs, + buttonAction: ButtonAction.third, + onTap: () async { + final zipFilePath = await getZippedLogsFile(); + await exportLogs(context, zipFilePath); + }, + ), + ButtonWidget( + isInAlert: true, + buttonType: ButtonType.secondary, + labelText: context.strings.cancel, + buttonAction: ButtonAction.cancel, + ), + ], + ); +} + +Future _sendLogs( BuildContext context, String toEmail, String? subject, From 9bb084d610781858e89b6b85ef64687b4387723e Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 20:24:37 +0530 Subject: [PATCH 019/164] Add typedefs to base --- mobile/packages/base/lib/ente_base.dart | 2 ++ mobile/packages/base/lib/typedefs.dart | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 mobile/packages/base/lib/typedefs.dart diff --git a/mobile/packages/base/lib/ente_base.dart b/mobile/packages/base/lib/ente_base.dart index 6f9e6961da..ed2182bd4d 100644 --- a/mobile/packages/base/lib/ente_base.dart +++ b/mobile/packages/base/lib/ente_base.dart @@ -4,3 +4,5 @@ export 'models/database.dart'; export 'models/key_attributes.dart'; export 'models/key_gen_result.dart'; export 'models/private_key_attributes.dart'; + +export 'typedefs.dart'; diff --git a/mobile/packages/base/lib/typedefs.dart b/mobile/packages/base/lib/typedefs.dart new file mode 100644 index 0000000000..7a42a1fe5e --- /dev/null +++ b/mobile/packages/base/lib/typedefs.dart @@ -0,0 +1,7 @@ +import 'dart:async'; + +typedef FutureVoidCallback = Future Function(); +typedef BoolCallBack = bool Function(); +typedef FutureVoidCallbackParamStr = Future Function(String); +typedef VoidCallbackParamStr = void Function(String); +typedef FutureOrVoidCallback = FutureOr Function(); From c392ad5dcb943a1d3f740cf86210b861441068f6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 20:24:52 +0530 Subject: [PATCH 020/164] Update utils --- mobile/packages/utils/lib/debouncer.dart | 34 ++++ mobile/packages/utils/lib/ente_utils.dart | 3 + mobile/packages/utils/lib/fake_progress.dart | 44 ++++++ .../packages/utils/lib/navigation_util.dart | 149 ++++++++++++++++++ 4 files changed, 230 insertions(+) create mode 100644 mobile/packages/utils/lib/debouncer.dart create mode 100644 mobile/packages/utils/lib/fake_progress.dart create mode 100644 mobile/packages/utils/lib/navigation_util.dart diff --git a/mobile/packages/utils/lib/debouncer.dart b/mobile/packages/utils/lib/debouncer.dart new file mode 100644 index 0000000000..fe1d300cbf --- /dev/null +++ b/mobile/packages/utils/lib/debouncer.dart @@ -0,0 +1,34 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class Debouncer { + final Duration _duration; + final ValueNotifier _debounceActiveNotifier = ValueNotifier(false); + Timer? _debounceTimer; + + Debouncer(this._duration); + + void run(Future Function() fn) { + if (isActive()) { + _debounceTimer!.cancel(); + } + _debounceTimer = Timer(_duration, () async { + await fn(); + _debounceActiveNotifier.value = false; + }); + _debounceActiveNotifier.value = true; + } + + void cancelDebounce() { + if (_debounceTimer != null) { + _debounceTimer!.cancel(); + } + } + + bool isActive() => _debounceTimer != null && _debounceTimer!.isActive; + + ValueNotifier get debounceActiveNotifier { + return _debounceActiveNotifier; + } +} diff --git a/mobile/packages/utils/lib/ente_utils.dart b/mobile/packages/utils/lib/ente_utils.dart index b431dc7ff9..1f18b2af82 100644 --- a/mobile/packages/utils/lib/ente_utils.dart +++ b/mobile/packages/utils/lib/ente_utils.dart @@ -1,6 +1,9 @@ library ente_utils; +export 'debouncer.dart'; export 'directory_utils.dart'; export 'email_util.dart'; +export 'fake_progress.dart'; export 'platform_util.dart'; export 'share_utils.dart'; +export 'navigation_util.dart'; diff --git a/mobile/packages/utils/lib/fake_progress.dart b/mobile/packages/utils/lib/fake_progress.dart new file mode 100644 index 0000000000..9f4a847918 --- /dev/null +++ b/mobile/packages/utils/lib/fake_progress.dart @@ -0,0 +1,44 @@ +import 'dart:async'; + +import "package:flutter/foundation.dart"; + +typedef FakeProgressCallback = void Function(int count); + +class FakePeriodicProgress { + final FakeProgressCallback? callback; + final Duration duration; + Timer? _timer; + bool _shouldRun = true; + int runCount = 0; + + FakePeriodicProgress({ + required this.callback, + required this.duration, + }); + + void start() { + assert(_shouldRun, "Cannot start a stopped FakePeriodicProgress"); + Future.delayed(duration, _invokePeriodically); + } + + void stop() { + if (_shouldRun) { + _shouldRun = false; + _timer?.cancel(); + } + } + + void _invokePeriodically() { + if (_shouldRun) { + try { + runCount++; + callback?.call(runCount); + } catch (e) { + debugPrint("Error in FakePeriodicProgress callback: $e"); + stop(); + return; + } + _timer = Timer(duration, _invokePeriodically); + } + } +} diff --git a/mobile/packages/utils/lib/navigation_util.dart b/mobile/packages/utils/lib/navigation_util.dart new file mode 100644 index 0000000000..58aa0fb852 --- /dev/null +++ b/mobile/packages/utils/lib/navigation_util.dart @@ -0,0 +1,149 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; + +Future routeToPage( + BuildContext context, + Widget page, { + bool forceCustomPageRoute = false, +}) { + if (Platform.isAndroid || forceCustomPageRoute) { + return Navigator.of(context).push( + _buildPageRoute(page), + ); + } else { + return Navigator.of(context).push( + SwipeableRouteBuilder( + pageBuilder: (context, animation, secondaryAnimation) { + return page; + }, + ), + ); + } +} + +void replacePage(BuildContext context, Widget page) { + Navigator.of(context).pushReplacement( + _buildPageRoute(page), + ); +} + +PageRouteBuilder _buildPageRoute(Widget page) { + return PageRouteBuilder( + pageBuilder: ( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return page; + }, + transitionsBuilder: ( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return Align( + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + transitionDuration: const Duration(milliseconds: 200), + opaque: false, + ); +} + +class SwipeableRouteBuilder extends PageRoute { + final RoutePageBuilder pageBuilder; + final PageTransitionsBuilder matchingBuilder = + const CupertinoPageTransitionsBuilder(); // Default iOS/macOS (to get the swipe right to go back gesture) + // final PageTransitionsBuilder matchingBuilder = const FadeUpwardsPageTransitionsBuilder(); // Default Android/Linux/Windows + + SwipeableRouteBuilder({required this.pageBuilder}); + + @override + Null get barrierColor => null; + + @override + Null get barrierLabel => null; + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return pageBuilder(context, animation, secondaryAnimation); + } + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration( + milliseconds: 300, + ); // Can give custom Duration, unlike in MaterialPageRoute + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return matchingBuilder.buildTransitions( + this, + context, + animation, + secondaryAnimation, + child, + ); + } + + @override + bool get opaque => false; +} + +class TransparentRoute extends PageRoute { + TransparentRoute({ + required this.builder, + super.settings, + }) : assert(builder != null), + super(fullscreenDialog: false); + + final WidgetBuilder? builder; + + @override + bool get opaque => false; + + @override + Null get barrierColor => null; + + @override + Null get barrierLabel => null; + + @override + bool get maintainState => true; + + @override + Duration get transitionDuration => const Duration(milliseconds: 200); + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + final result = builder!(context); + return FadeTransition( + opacity: Tween(begin: 0, end: 1).animate(animation), + child: Semantics( + scopesRoute: true, + explicitChildNodes: true, + child: result, + ), + ); + } +} From e8a9e509a86a968450f76d4d99812cc1e7f44ee2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 20:25:02 +0530 Subject: [PATCH 021/164] Update common strings --- mobile/packages/strings/lib/l10n/arb/strings_en.arb | 3 ++- mobile/packages/strings/lib/l10n/strings_localizations.dart | 6 ++++++ .../packages/strings/lib/l10n/strings_localizations_ar.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_bg.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_cs.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_da.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_el.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_en.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_es.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_fr.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_id.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_ja.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_ko.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_lt.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_nl.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_pl.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_pt.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_ru.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_sk.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_sr.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_sv.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_tr.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_vi.dart | 5 +++++ .../packages/strings/lib/l10n/strings_localizations_zh.dart | 5 +++++ 24 files changed, 118 insertions(+), 1 deletion(-) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 56bdf32690..a7175cb422 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -118,5 +118,6 @@ "viewLogs": "View logs", "@viewLogs": { "description": "Button to view logs" - } + }, + "customEndpoint": "Connected to {endpoint}" } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index ea43fc9a2d..ab02835b86 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -301,6 +301,12 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'View logs'** String get viewLogs; + + /// No description provided for @customEndpoint. + /// + /// In en, this message translates to: + /// **'Connected to {endpoint}'** + String customEndpoint(Object endpoint); } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 39f79d3e4f..fccdc52045 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -97,4 +97,9 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 1987d786e1..92d03e2e38 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -97,4 +97,9 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index 9f37112311..c641b12607 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -97,4 +97,9 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index 5458a85d41..de3b14b5b6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -97,4 +97,9 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 45d4bcacc8..de3d222597 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -97,4 +97,9 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index c09af89599..ab264897c9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -97,4 +97,9 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 0861a80978..9ffde6ed0d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -97,4 +97,9 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index e99292c174..ab7d35b1b8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -97,4 +97,9 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index ea282f632e..ce8f373a04 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -97,4 +97,9 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index c13bfb5ef2..69f0c19d40 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -97,4 +97,9 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index ef135b61d4..c572a95eae 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -97,4 +97,9 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 7b9ba7eb34..4353c016d8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -97,4 +97,9 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 220cdd4f55..276a6d4866 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -97,4 +97,9 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index 9b2d7f5cbf..c8ffbcd073 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -97,4 +97,9 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index 1960289a34..7190ab436c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -97,4 +97,9 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index c326fbce72..c5345f29dd 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -97,4 +97,9 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 85bed4aa92..30934edbc7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -97,4 +97,9 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index d909c41cc0..f06875959f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -97,4 +97,9 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 2da70572f9..0e69b88706 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -97,4 +97,9 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index 81a3b52801..ae94a5d61e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -97,4 +97,9 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 983a5f28a9..68048de3fb 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -97,4 +97,9 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 7cf2e36d58..41dd769a4b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -96,6 +96,11 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get viewLogs => 'View logs'; + + @override + String customEndpoint(Object endpoint) { + return 'Connected to $endpoint'; + } } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From 490759243b877fd82693514c7e144d57d85d2791 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 20:25:18 +0530 Subject: [PATCH 022/164] Setup common UI components --- mobile/packages/ui/analysis_options.yaml | 10 + .../lib/components/action_sheet_widget.dart | 221 ++++ .../lib/components/buttons/button_widget.dart | 529 +++++++++ .../lib/components/buttons/dynamic_fab.dart | 92 ++ .../components/buttons/gradient_button.dart | 81 ++ .../buttons/icon_button_widget.dart | 109 ++ .../buttons/models/button_result.dart | 11 + .../buttons/models/button_type.dart | 205 ++++ .../buttons/models/custom_button_style.dart | 33 + .../lib/components/captioned_text_widget.dart | 57 + .../lib/components/components_constants.dart | 4 + .../components/developer_settings_widget.dart | 32 + .../ui/lib/components/dialog_widget.dart | 291 +++++ .../packages/ui/lib/components/dialogs.dart | 124 ++ .../ui/lib/components/divider_widget.dart | 69 ++ .../ui/lib/components/loading_widget.dart | 33 + .../components/menu_item_child_widgets.dart | 173 +++ .../ui/lib/components/menu_item_widget.dart | 291 +++++ .../ui/lib/components/progress_dialog.dart | 289 +++++ .../ui/lib/components/separators.dart | 13 + .../ui/lib/components/text_input_widget.dart | 406 +++++++ .../components/title_bar_title_widget.dart | 55 + .../ui/lib/components/title_bar_widget.dart | 152 +++ .../lib/components/toggle_switch_widget.dart | 136 +++ .../ui/lib/lifecycle_event_handler.dart | 31 + .../ui/lib/models/execution_states.dart | 6 + .../ui/lib/pages/log_file_viewer.dart | 65 ++ mobile/packages/ui/lib/pages/web_page.dart | 43 + .../ui/lib/theme/color_migration_demo.dart | 206 ++++ .../ui/lib/theme/color_system_test.dart | 161 +++ mobile/packages/ui/lib/theme/colors.dart | 832 ++++++++++++++ mobile/packages/ui/lib/theme/effects.dart | 59 + mobile/packages/ui/lib/theme/ente_theme.dart | 85 ++ .../ui/lib/theme/ente_theme_data.dart | 537 +++++++++ .../ui/lib/theme/example_app_colors.dart | 152 +++ .../packages/ui/lib/theme/multi_app_demo.dart | 427 +++++++ .../ui/lib/theme/platform_text_config.dart | 75 ++ mobile/packages/ui/lib/theme/text_style.dart | 204 ++++ mobile/packages/ui/lib/utils/dialog_util.dart | 373 ++++++ .../ui/lib/utils/file_icon_utils.dart | 72 ++ mobile/packages/ui/lib/utils/toast_util.dart | 52 + .../ui/lib/utils/window_listener_service.dart | 41 + mobile/packages/ui/pubspec.lock | 1002 +++++++++++++++++ mobile/packages/ui/pubspec.yaml | 35 + 44 files changed, 7874 insertions(+) create mode 100644 mobile/packages/ui/analysis_options.yaml create mode 100644 mobile/packages/ui/lib/components/action_sheet_widget.dart create mode 100644 mobile/packages/ui/lib/components/buttons/button_widget.dart create mode 100644 mobile/packages/ui/lib/components/buttons/dynamic_fab.dart create mode 100644 mobile/packages/ui/lib/components/buttons/gradient_button.dart create mode 100644 mobile/packages/ui/lib/components/buttons/icon_button_widget.dart create mode 100644 mobile/packages/ui/lib/components/buttons/models/button_result.dart create mode 100644 mobile/packages/ui/lib/components/buttons/models/button_type.dart create mode 100644 mobile/packages/ui/lib/components/buttons/models/custom_button_style.dart create mode 100644 mobile/packages/ui/lib/components/captioned_text_widget.dart create mode 100644 mobile/packages/ui/lib/components/components_constants.dart create mode 100644 mobile/packages/ui/lib/components/developer_settings_widget.dart create mode 100644 mobile/packages/ui/lib/components/dialog_widget.dart create mode 100644 mobile/packages/ui/lib/components/dialogs.dart create mode 100644 mobile/packages/ui/lib/components/divider_widget.dart create mode 100644 mobile/packages/ui/lib/components/loading_widget.dart create mode 100644 mobile/packages/ui/lib/components/menu_item_child_widgets.dart create mode 100644 mobile/packages/ui/lib/components/menu_item_widget.dart create mode 100644 mobile/packages/ui/lib/components/progress_dialog.dart create mode 100644 mobile/packages/ui/lib/components/separators.dart create mode 100644 mobile/packages/ui/lib/components/text_input_widget.dart create mode 100644 mobile/packages/ui/lib/components/title_bar_title_widget.dart create mode 100644 mobile/packages/ui/lib/components/title_bar_widget.dart create mode 100644 mobile/packages/ui/lib/components/toggle_switch_widget.dart create mode 100644 mobile/packages/ui/lib/lifecycle_event_handler.dart create mode 100644 mobile/packages/ui/lib/models/execution_states.dart create mode 100644 mobile/packages/ui/lib/pages/log_file_viewer.dart create mode 100644 mobile/packages/ui/lib/pages/web_page.dart create mode 100644 mobile/packages/ui/lib/theme/color_migration_demo.dart create mode 100644 mobile/packages/ui/lib/theme/color_system_test.dart create mode 100644 mobile/packages/ui/lib/theme/colors.dart create mode 100644 mobile/packages/ui/lib/theme/effects.dart create mode 100644 mobile/packages/ui/lib/theme/ente_theme.dart create mode 100644 mobile/packages/ui/lib/theme/ente_theme_data.dart create mode 100644 mobile/packages/ui/lib/theme/example_app_colors.dart create mode 100644 mobile/packages/ui/lib/theme/multi_app_demo.dart create mode 100644 mobile/packages/ui/lib/theme/platform_text_config.dart create mode 100644 mobile/packages/ui/lib/theme/text_style.dart create mode 100644 mobile/packages/ui/lib/utils/dialog_util.dart create mode 100644 mobile/packages/ui/lib/utils/file_icon_utils.dart create mode 100644 mobile/packages/ui/lib/utils/toast_util.dart create mode 100644 mobile/packages/ui/lib/utils/window_listener_service.dart create mode 100644 mobile/packages/ui/pubspec.lock create mode 100644 mobile/packages/ui/pubspec.yaml diff --git a/mobile/packages/ui/analysis_options.yaml b/mobile/packages/ui/analysis_options.yaml new file mode 100644 index 0000000000..609eb5d8aa --- /dev/null +++ b/mobile/packages/ui/analysis_options.yaml @@ -0,0 +1,10 @@ +include: package:flutter_lints/flutter.yaml + +linter: + rules: + - always_declare_return_types + - always_put_required_named_parameters_first + - avoid_print + - prefer_const_constructors + - prefer_const_literals_to_create_immutables + - use_key_in_widget_constructors diff --git a/mobile/packages/ui/lib/components/action_sheet_widget.dart b/mobile/packages/ui/lib/components/action_sheet_widget.dart new file mode 100644 index 0000000000..fbb34337a5 --- /dev/null +++ b/mobile/packages/ui/lib/components/action_sheet_widget.dart @@ -0,0 +1,221 @@ +import 'dart:ui'; + +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_result.dart'; +import 'package:ente_ui/components/components_constants.dart'; +import 'package:ente_ui/components/separators.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/effects.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +enum ActionSheetType { + defaultActionSheet, + iconOnly, +} + +///Returns null if dismissed +Future showActionSheet({ + required BuildContext context, + required List buttons, + ActionSheetType actionSheetType = ActionSheetType.defaultActionSheet, + bool enableDrag = true, + bool isDismissible = true, + bool isCheckIconGreen = false, + String? title, + Widget? bodyWidget, + String? body, + String? bodyHighlight, +}) { + return showMaterialModalBottomSheet( + backgroundColor: Colors.transparent, + barrierColor: backdropFaintDark, + useRootNavigator: true, + context: context, + isDismissible: isDismissible, + enableDrag: enableDrag, + builder: (_) { + return ActionSheetWidget( + title: title, + bodyWidget: bodyWidget, + body: body, + bodyHighlight: bodyHighlight, + actionButtons: buttons, + actionSheetType: actionSheetType, + isCheckIconGreen: isCheckIconGreen, + ); + }, + ); +} + +class ActionSheetWidget extends StatelessWidget { + final String? title; + final Widget? bodyWidget; + final String? body; + final String? bodyHighlight; + final List actionButtons; + final ActionSheetType actionSheetType; + final bool isCheckIconGreen; + + const ActionSheetWidget({ + required this.actionButtons, + required this.actionSheetType, + required this.isCheckIconGreen, + this.title, + this.bodyWidget, + this.body, + this.bodyHighlight, + super.key, + }); + + @override + Widget build(BuildContext context) { + final isTitleAndBodyNull = + title == null && bodyWidget == null && body == null; + final blur = MediaQuery.of(context).platformBrightness == Brightness.light + ? blurMuted + : blurBase; + final extraWidth = MediaQuery.of(context).size.width - restrictedMaxWidth; + final double? horizontalPadding = extraWidth > 0 ? extraWidth / 2 : null; + return Padding( + padding: EdgeInsets.fromLTRB( + horizontalPadding ?? 12, + 12, + horizontalPadding ?? 12, + 32, + ), + child: Container( + decoration: BoxDecoration(boxShadow: shadowMenuLight), + child: ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: blur, sigmaY: blur), + child: Container( + color: backdropMutedDark, + child: Padding( + padding: EdgeInsets.fromLTRB( + 24, + 24, + 24, + isTitleAndBodyNull ? 24 : 28, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + isTitleAndBodyNull + ? const SizedBox.shrink() + : Padding( + padding: const EdgeInsets.only(bottom: 36), + child: ContentContainerWidget( + title: title, + bodyWidget: bodyWidget, + body: body, + bodyHighlight: bodyHighlight, + actionSheetType: actionSheetType, + isCheckIconGreen: isCheckIconGreen, + ), + ), + ActionButtons( + actionButtons, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} + +class ContentContainerWidget extends StatelessWidget { + final String? title; + final Widget? bodyWidget; + final String? body; + final String? bodyHighlight; + final ActionSheetType actionSheetType; + final bool isCheckIconGreen; + + const ContentContainerWidget({ + required this.actionSheetType, + required this.isCheckIconGreen, + this.title, + this.bodyWidget, + this.body, + this.bodyHighlight, + super.key, + }); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final bool bodyMissing = body == null && bodyWidget == null; + debugPrint("body missing $bodyMissing"); + return Column( + mainAxisSize: MainAxisSize.min, + //todo: set cross axis to center when icon should be shown in place of body + crossAxisAlignment: actionSheetType == ActionSheetType.defaultActionSheet + ? CrossAxisAlignment.stretch + : CrossAxisAlignment.center, + children: [ + title == null + ? const SizedBox.shrink() + : Text( + title!, + style: textTheme.largeBold + .copyWith(color: textBaseDark), //constant color + ), + title == null || bodyMissing + ? const SizedBox.shrink() + : const SizedBox(height: 19), + actionSheetType == ActionSheetType.defaultActionSheet + ? bodyMissing + ? const SizedBox.shrink() + : (bodyWidget != null + ? bodyWidget! + : Text( + body!, + style: textTheme.body + .copyWith(color: textMutedDark), //constant color + )) + : Icon( + Icons.check_outlined, + size: 48, + color: isCheckIconGreen + ? getEnteColorScheme(context).primary700 + : strokeBaseDark, + ), + actionSheetType == ActionSheetType.defaultActionSheet && + bodyHighlight != null + ? Padding( + padding: const EdgeInsets.only(top: 19.0), + child: Text( + bodyHighlight!, + style: textTheme.body + .copyWith(color: textBaseDark), //constant color + ), + ) + : const SizedBox.shrink(), + ], + ); + } +} + +class ActionButtons extends StatelessWidget { + final List actionButtons; + const ActionButtons(this.actionButtons, {super.key}); + + @override + Widget build(BuildContext context) { + final actionButtonsWithSeparators = actionButtons; + return Column( + children: + //Separator height is 8pts in figma. -2pts here as the action + //buttons are 2pts extra in height in code compared to figma because + //of the border(1pt top + 1pt bottom) of action buttons. + addSeparators(actionButtonsWithSeparators, const SizedBox(height: 6)), + ); + } +} diff --git a/mobile/packages/ui/lib/components/buttons/button_widget.dart b/mobile/packages/ui/lib/components/buttons/button_widget.dart new file mode 100644 index 0000000000..29efd4c99c --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/button_widget.dart @@ -0,0 +1,529 @@ +import 'package:ente_base/typedefs.dart'; +import 'package:ente_ui/components/buttons/models/button_result.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/buttons/models/custom_button_style.dart'; +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/models/execution_states.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/theme/text_style.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:ente_utils/debouncer.dart'; + +enum ButtonSize { small, large } + +enum ButtonAction { first, second, third, fourth, cancel, error } + +class ButtonWidget extends StatelessWidget { + final IconData? icon; + final String? labelText; + final ButtonType buttonType; + final FutureVoidCallback? onTap; + final bool isDisabled; + final ButtonSize buttonSize; + + ///Setting this flag to true will show a success confirmation as a 'check' + ///icon once the onTap(). This is expected to be used only if time taken to + ///execute onTap() takes less than debouce time. + final bool shouldShowSuccessConfirmation; + + ///Setting this flag to false will restrict the loading and success states of + ///the button from surfacing on the UI. The ExecutionState of the button will + ///change irrespective of the value of this flag. Only that it won't be + ///surfaced on the UI + final bool shouldSurfaceExecutionStates; + + /// iconColor should only be specified when we do not want to honor the default + /// iconColor based on buttonType. Most of the items, default iconColor is what + /// we need unless we want to pop out the icon in a non-primary button type + final Color? iconColor; + + ///Button action will only work if isInAlert is true + final ButtonAction? buttonAction; + + ///setting this flag to true will make the button appear like how it would + ///on dark theme irrespective of the app's theme. + final bool shouldStickToDarkTheme; + + ///isInAlert is to dismiss the alert if the action on the button is completed. + ///This should be set to true if the alert which uses this button needs to + ///return the Button's action. + final bool isInAlert; + + /// progressStatus can be used to display information about the action + /// progress when ExecutionState is in Progress. + final ValueNotifier? progressStatus; + + const ButtonWidget({ + super.key, + required this.buttonType, + this.buttonSize = ButtonSize.large, + this.icon, + this.labelText, + this.onTap, + this.shouldStickToDarkTheme = false, + this.isDisabled = false, + this.buttonAction, + this.isInAlert = false, + this.iconColor, + this.shouldSurfaceExecutionStates = true, + this.progressStatus, + this.shouldShowSuccessConfirmation = false, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = + shouldStickToDarkTheme ? darkScheme : getEnteColorScheme(context); + final inverseColorScheme = shouldStickToDarkTheme + ? lightScheme + : getEnteColorScheme(context, inverse: true); + final textTheme = + shouldStickToDarkTheme ? darkTextTheme : getEnteTextTheme(context); + final inverseTextTheme = shouldStickToDarkTheme + ? lightTextTheme + : getEnteTextTheme(context, inverse: true); + final buttonStyle = CustomButtonStyle( + //Dummy default values since we need to keep these properties non-nullable + defaultButtonColor: Colors.transparent, + defaultBorderColor: Colors.transparent, + defaultIconColor: Colors.transparent, + defaultLabelStyle: textTheme.body, + ); + buttonStyle.defaultButtonColor = buttonType.defaultButtonColor(colorScheme); + buttonStyle.pressedButtonColor = buttonType.pressedButtonColor(colorScheme); + buttonStyle.disabledButtonColor = + buttonType.disabledButtonColor(colorScheme, buttonSize); + buttonStyle.defaultBorderColor = + buttonType.defaultBorderColor(colorScheme, buttonSize); + buttonStyle.pressedBorderColor = buttonType.pressedBorderColor( + colorScheme: colorScheme, + buttonSize: buttonSize, + ); + buttonStyle.disabledBorderColor = + buttonType.disabledBorderColor(colorScheme, buttonSize); + buttonStyle.defaultIconColor = iconColor ?? + buttonType.defaultIconColor( + colorScheme: colorScheme, + inverseColorScheme: inverseColorScheme, + ); + buttonStyle.pressedIconColor = + buttonType.pressedIconColor(colorScheme, buttonSize); + buttonStyle.disabledIconColor = + buttonType.disabledIconColor(colorScheme, buttonSize); + buttonStyle.defaultLabelStyle = buttonType.defaultLabelStyle( + textTheme: textTheme, + inverseTextTheme: inverseTextTheme, + ); + buttonStyle.pressedLabelStyle = + buttonType.pressedLabelStyle(textTheme, colorScheme, buttonSize); + buttonStyle.disabledLabelStyle = + buttonType.disabledLabelStyle(textTheme, colorScheme); + buttonStyle.checkIconColor = buttonType.checkIconColor(colorScheme); + + return ButtonChildWidget( + buttonStyle: buttonStyle, + buttonType: buttonType, + isDisabled: isDisabled, + buttonSize: buttonSize, + isInAlert: isInAlert, + onTap: onTap, + labelText: labelText, + icon: icon, + buttonAction: buttonAction, + shouldSurfaceExecutionStates: shouldSurfaceExecutionStates, + progressStatus: progressStatus, + shouldShowSuccessConfirmation: shouldShowSuccessConfirmation, + ); + } +} + +class ButtonChildWidget extends StatefulWidget { + final CustomButtonStyle buttonStyle; + final FutureVoidCallback? onTap; + final ButtonType buttonType; + final String? labelText; + final IconData? icon; + final bool isDisabled; + final ButtonSize buttonSize; + final ButtonAction? buttonAction; + final bool isInAlert; + final bool shouldSurfaceExecutionStates; + final ValueNotifier? progressStatus; + final bool shouldShowSuccessConfirmation; + + const ButtonChildWidget({ + super.key, + required this.buttonStyle, + required this.buttonType, + required this.isDisabled, + required this.buttonSize, + required this.isInAlert, + required this.shouldSurfaceExecutionStates, + required this.shouldShowSuccessConfirmation, + this.progressStatus, + this.onTap, + this.labelText, + this.icon, + this.buttonAction, + }); + + @override + State createState() => _ButtonChildWidgetState(); +} + +class _ButtonChildWidgetState extends State { + late Color buttonColor; + late Color borderColor; + late Color iconColor; + late TextStyle labelStyle; + late Color checkIconColor; + late Color loadingIconColor; + ValueNotifier? progressStatus; + + ///This is used to store the width of the button in idle state (small button) + ///to be used as width for the button when the loading/succes states comes. + double? widthOfButton; + final _debouncer = Debouncer(const Duration(milliseconds: 300)); + ExecutionState executionState = ExecutionState.idle; + Exception? _exception; + + @override + void initState() { + _setButtonTheme(); + super.initState(); + } + + @override + void didUpdateWidget(covariant ButtonChildWidget oldWidget) { + _setButtonTheme(); + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + if (executionState == ExecutionState.successful) { + Future.delayed(Duration(seconds: widget.isInAlert ? 1 : 2), () { + setState(() { + executionState = ExecutionState.idle; + }); + }); + } + return GestureDetector( + onTap: _shouldRegisterGestures ? _onTap : null, + onTapDown: _shouldRegisterGestures ? _onTapDown : null, + onTapUp: _shouldRegisterGestures ? _onTapUp : null, + onTapCancel: _shouldRegisterGestures ? _onTapCancel : null, + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(4)), + border: widget.buttonType == ButtonType.tertiaryCritical + ? Border.all(color: borderColor) + : null, + ), + child: AnimatedContainer( + duration: const Duration(milliseconds: 16), + width: widget.buttonSize == ButtonSize.large ? double.infinity : null, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(4)), + color: buttonColor, + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 14, horizontal: 16), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInOutExpo, + switchOutCurve: Curves.easeInOutExpo, + child: executionState == ExecutionState.idle || + !widget.shouldSurfaceExecutionStates + ? widget.buttonType.hasTrailingIcon + ? Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + widget.labelText == null + ? const SizedBox.shrink() + : Flexible( + child: Padding( + padding: widget.icon == null + ? const EdgeInsets.symmetric( + horizontal: 8, + ) + : const EdgeInsets.only(right: 16), + child: Text( + widget.labelText!, + overflow: TextOverflow.ellipsis, + maxLines: 2, + style: labelStyle, + ), + ), + ), + widget.icon == null + ? const SizedBox.shrink() + : Icon( + widget.icon, + size: 20, + color: iconColor, + ), + ], + ) + : Builder( + builder: (context) { + SchedulerBinding.instance.addPostFrameCallback( + (timeStamp) { + final box = + context.findRenderObject() as RenderBox; + widthOfButton = box.size.width; + }, + ); + return Row( + mainAxisSize: + widget.buttonSize == ButtonSize.large + ? MainAxisSize.max + : MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + widget.icon == null + ? const SizedBox.shrink() + : Icon( + widget.icon, + size: 20, + color: iconColor, + ), + widget.icon == null || widget.labelText == null + ? const SizedBox.shrink() + : const SizedBox(width: 8), + widget.labelText == null + ? const SizedBox.shrink() + : Flexible( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, + ), + child: Text( + widget.labelText!, + style: labelStyle, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + ), + ], + ); + }, + ) + : executionState == ExecutionState.inProgress + ? SizedBox( + width: widthOfButton, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + progressStatus == null + ? const SizedBox.shrink() + : ValueListenableBuilder( + valueListenable: progressStatus!, + builder: ( + BuildContext context, + String value, + Widget? child, + ) { + return Padding( + padding: + const EdgeInsets.only(right: 8.0), + child: Text( + value, + style: lightTextTheme.smallBold, + ), + ); + }, + ), + EnteLoadingWidget( + padding: 3, + color: loadingIconColor, + ), + ], + ), + ) + : executionState == ExecutionState.successful + ? SizedBox( + width: widthOfButton, + child: Icon( + Icons.check_outlined, + size: 20, + color: checkIconColor, + ), + ) + : const SizedBox.shrink(), //fallback + ), + ), + ), + ), + ); + } + + void _setButtonTheme() { + progressStatus = widget.progressStatus; + checkIconColor = widget.buttonStyle.checkIconColor ?? + widget.buttonStyle.defaultIconColor; + loadingIconColor = widget.buttonStyle.defaultIconColor; + if (widget.isDisabled) { + buttonColor = widget.buttonStyle.disabledButtonColor ?? + widget.buttonStyle.defaultButtonColor; + borderColor = widget.buttonStyle.disabledBorderColor ?? + widget.buttonStyle.defaultBorderColor; + iconColor = widget.buttonStyle.disabledIconColor ?? + widget.buttonStyle.defaultIconColor; + labelStyle = widget.buttonStyle.disabledLabelStyle ?? + widget.buttonStyle.defaultLabelStyle; + } else { + buttonColor = widget.buttonStyle.defaultButtonColor; + borderColor = widget.buttonStyle.defaultBorderColor; + iconColor = widget.buttonStyle.defaultIconColor; + labelStyle = widget.buttonStyle.defaultLabelStyle; + } + } + + bool get _shouldRegisterGestures => + !widget.isDisabled && executionState == ExecutionState.idle; + + void _onTap() async { + if (widget.onTap != null) { + _debouncer.run( + () => Future(() { + setState(() { + executionState = ExecutionState.inProgress; + }); + }), + ); + await widget.onTap!.call().then( + (value) { + _exception = null; + }, + onError: (error, stackTrace) { + executionState = ExecutionState.error; + _exception = error as Exception; + _debouncer.cancelDebounce(); + }, + ); + widget.shouldShowSuccessConfirmation && _debouncer.isActive() + ? executionState = ExecutionState.successful + : null; + _debouncer.cancelDebounce(); + if (executionState == ExecutionState.successful) { + setState(() {}); + } + + // when the time taken by widget.onTap is approximately equal to the debounce + // time, the callback is getting executed when/after the if condition + // below is executing/executed which results in execution state stuck at + // idle state. This Future is for delaying the execution of the if + // condition so that the calback in the debouncer finishes execution before. + await Future.delayed(const Duration(milliseconds: 5)); + } + if (executionState == ExecutionState.inProgress || + executionState == ExecutionState.error) { + if (executionState == ExecutionState.inProgress) { + if (mounted) { + setState(() { + executionState = ExecutionState.successful; + Future.delayed( + Duration( + seconds: widget.shouldSurfaceExecutionStates + ? (widget.isInAlert ? 1 : 2) + : 0, + ), () { + widget.isInAlert + ? _popWithButtonAction( + context, + buttonAction: widget.buttonAction, + ) + : null; + if (mounted) { + setState(() { + executionState = ExecutionState.idle; + }); + } + }); + }); + } + } + if (executionState == ExecutionState.error) { + setState(() { + executionState = ExecutionState.idle; + widget.isInAlert + ? Future.delayed( + const Duration(seconds: 0), + () => _popWithButtonAction( + context, + buttonAction: ButtonAction.error, + exception: _exception, + ), + ) + : null; + }); + } + } else { + if (widget.isInAlert) { + Future.delayed( + Duration(seconds: widget.shouldShowSuccessConfirmation ? 1 : 0), + () => + _popWithButtonAction(context, buttonAction: widget.buttonAction), + ); + } + } + } + + void _popWithButtonAction( + BuildContext context, { + required ButtonAction? buttonAction, + Exception? exception, + }) { + if (Navigator.of(context).canPop()) { + Navigator.of(context).pop(ButtonResult(widget.buttonAction, exception)); + } else if (exception != null) { + //This is to show the execution was unsuccessful if the dialog is manually + //closed before the execution completes. + showGenericErrorDialog( + context: context, + error: exception, + ); + } + } + + void _onTapDown(details) { + setState(() { + buttonColor = widget.buttonStyle.pressedButtonColor ?? + widget.buttonStyle.defaultButtonColor; + borderColor = widget.buttonStyle.pressedBorderColor ?? + widget.buttonStyle.defaultBorderColor; + iconColor = widget.buttonStyle.pressedIconColor ?? + widget.buttonStyle.defaultIconColor; + labelStyle = widget.buttonStyle.pressedLabelStyle ?? + widget.buttonStyle.defaultLabelStyle; + }); + } + + void _onTapUp(details) { + Future.delayed( + const Duration(milliseconds: 84), + () => setState(() { + setAllStylesToDefault(); + }), + ); + } + + void _onTapCancel() { + setState(() { + setAllStylesToDefault(); + }); + } + + void setAllStylesToDefault() { + buttonColor = widget.buttonStyle.defaultButtonColor; + borderColor = widget.buttonStyle.defaultBorderColor; + iconColor = widget.buttonStyle.defaultIconColor; + labelStyle = widget.buttonStyle.defaultLabelStyle; + } +} diff --git a/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart new file mode 100644 index 0000000000..1ff4aab191 --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart @@ -0,0 +1,92 @@ +import 'dart:math' as math; + +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class DynamicFAB extends StatelessWidget { + final bool? isKeypadOpen; + final bool? isFormValid; + final String? buttonText; + final Function? onPressedFunction; + + const DynamicFAB({ + super.key, + this.isKeypadOpen, + this.buttonText, + this.isFormValid, + this.onPressedFunction, + }); + + @override + Widget build(BuildContext context) { + if (isKeypadOpen!) { + return Container( + decoration: BoxDecoration( + boxShadow: [ + BoxShadow( + color: getEnteColorScheme(context).alternativeColor, + spreadRadius: 200, + blurRadius: 100, + offset: const Offset(0, 230), + ), + ], + ), + width: double.infinity, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + FloatingActionButton( + heroTag: 'FAB', + backgroundColor: + getEnteColorScheme(context).dynamicFABBackgroundColor, + foregroundColor: getEnteColorScheme(context).dynamicFABTextColor, + onPressed: isFormValid! + ? onPressedFunction as void Function()? + : () { + FocusScope.of(context).unfocus(); + }, + child: Transform.rotate( + angle: isFormValid! ? 0 : math.pi / 2, + child: const Icon( + Icons.chevron_right, + size: 36, + ), + ), //keypad down here + ), + ], + ), + ); + } else { + return Container( + width: double.infinity, + height: 56, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: OutlinedButton( + onPressed: + isFormValid! ? onPressedFunction as void Function()? : null, + child: Text(buttonText!, + style: isFormValid! + ? getEnteTextTheme(context).body + : getEnteTextTheme(context).bodyFaint), + ), + ); + } + } +} + +class NoScalingAnimation extends FloatingActionButtonAnimator { + @override + Offset getOffset({Offset? begin, required Offset end, double? progress}) { + return end; + } + + @override + Animation getRotationAnimation({required Animation parent}) { + return Tween(begin: 1.0, end: 1.0).animate(parent); + } + + @override + Animation getScaleAnimation({required Animation parent}) { + return Tween(begin: 1.0, end: 1.0).animate(parent); + } +} diff --git a/mobile/packages/ui/lib/components/buttons/gradient_button.dart b/mobile/packages/ui/lib/components/buttons/gradient_button.dart new file mode 100644 index 0000000000..b98c7894ef --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/gradient_button.dart @@ -0,0 +1,81 @@ +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class GradientButton extends StatelessWidget { + final Function? onTap; + + // text is ignored if child is specified + final String text; + + // nullable + final IconData? iconData; + + // padding between the text and icon + final double paddingValue; + + const GradientButton({ + super.key, + this.onTap, + this.text = '', + this.iconData, + this.paddingValue = 0.0, + }); + + @override + Widget build(BuildContext context) { + Widget buttonContent; + if (iconData == null) { + buttonContent = Text( + text, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontFamily: 'Inter-SemiBold', + fontSize: 18, + ), + ); + } else { + buttonContent = Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + iconData, + size: 20, + color: Colors.white, + ), + const Padding(padding: EdgeInsets.symmetric(horizontal: 6)), + Text( + text, + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.w600, + fontFamily: 'Inter-SemiBold', + fontSize: 18, + ), + ), + ], + ); + } + return InkWell( + onTap: onTap as void Function()?, + child: Container( + height: 56, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: const Alignment(0.1, -0.9), + end: const Alignment(-0.6, 0.9), + colors: onTap != null + ? getEnteColorScheme(context).gradientButtonBgColors + : [ + getEnteColorScheme(context).fillMuted, + getEnteColorScheme(context).fillMuted, + ], + ), + borderRadius: BorderRadius.circular(24), + ), + child: Center(child: buttonContent), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/buttons/icon_button_widget.dart b/mobile/packages/ui/lib/components/buttons/icon_button_widget.dart new file mode 100644 index 0000000000..922e69e7f0 --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/icon_button_widget.dart @@ -0,0 +1,109 @@ +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +enum IconButtonType { + primary, + secondary, + rounded, +} + +class IconButtonWidget extends StatefulWidget { + final IconButtonType iconButtonType; + final IconData icon; + final bool disableGestureDetector; + final VoidCallback? onTap; + final Color? defaultColor; + final Color? pressedColor; + final Color? iconColor; + const IconButtonWidget({ + super.key, + required this.icon, + required this.iconButtonType, + this.disableGestureDetector = false, + this.onTap, + this.defaultColor, + this.pressedColor, + this.iconColor, + }); + + @override + State createState() => _IconButtonWidgetState(); +} + +class _IconButtonWidgetState extends State { + Color? iconStateColor; + @override + void didChangeDependencies() { + setState(() { + iconStateColor = null; + }); + super.didChangeDependencies(); + } + + @override + Widget build(BuildContext context) { + final bool hasPressedState = widget.onTap != null; + final colorTheme = getEnteColorScheme(context); + iconStateColor ?? + (iconStateColor = widget.defaultColor ?? + (widget.iconButtonType == IconButtonType.rounded + ? colorTheme.fillFaint + : null)); + return widget.disableGestureDetector + ? _iconButton(colorTheme) + : GestureDetector( + onTapDown: hasPressedState ? _onTapDown : null, + onTapUp: hasPressedState ? _onTapUp : null, + onTapCancel: hasPressedState ? _onTapCancel : null, + onTap: widget.onTap, + child: _iconButton(colorTheme), + ); + } + + Widget _iconButton(EnteColorScheme colorTheme) { + return Padding( + padding: const EdgeInsets.all(4.0), + child: AnimatedContainer( + duration: const Duration(milliseconds: 20), + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + color: iconStateColor, + ), + child: Icon( + widget.icon, + color: widget.iconColor ?? + (widget.iconButtonType == IconButtonType.secondary + ? colorTheme.strokeMuted + : colorTheme.strokeBase), + size: 24, + ), + ), + ); + } + + _onTapDown(details) { + final colorTheme = getEnteColorScheme(context); + setState(() { + iconStateColor = widget.pressedColor ?? + (widget.iconButtonType == IconButtonType.rounded + ? colorTheme.fillMuted + : colorTheme.fillFaint); + }); + } + + _onTapUp(details) { + Future.delayed(const Duration(milliseconds: 100), () { + setState(() { + iconStateColor = null; + }); + }); + } + + _onTapCancel() { + setState(() { + iconStateColor = null; + }); + } +} diff --git a/mobile/packages/ui/lib/components/buttons/models/button_result.dart b/mobile/packages/ui/lib/components/buttons/models/button_result.dart new file mode 100644 index 0000000000..7560768dc1 --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/models/button_result.dart @@ -0,0 +1,11 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; + +class ButtonResult { + ///action can be null when action for the button that is returned when popping + ///the widget (dialog, actionSheet) which uses a ButtonWidget isn't + ///relevant/useful and so is not assigned a value when an instance of + ///ButtonWidget is created. + final ButtonAction? action; + final Exception? exception; + ButtonResult([this.action, this.exception]); +} diff --git a/mobile/packages/ui/lib/components/buttons/models/button_type.dart b/mobile/packages/ui/lib/components/buttons/models/button_type.dart new file mode 100644 index 0000000000..a6cb8d95eb --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/models/button_type.dart @@ -0,0 +1,205 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/text_style.dart'; +import 'package:flutter/material.dart'; + +enum ButtonType { + primary, + secondary, + neutral, + trailingIcon, + critical, + tertiaryCritical, + trailingIconPrimary, + trailingIconSecondary, + tertiary; + + bool get isPrimary => + this == ButtonType.primary || this == ButtonType.trailingIconPrimary; + + bool get hasTrailingIcon => + this == ButtonType.trailingIcon || + this == ButtonType.trailingIconPrimary || + this == ButtonType.trailingIconSecondary; + + bool get isSecondary => + this == ButtonType.secondary || this == ButtonType.trailingIconSecondary; + + bool get isCritical => + this == ButtonType.critical || this == ButtonType.tertiaryCritical; + + bool get isNeutral => + this == ButtonType.neutral || this == ButtonType.trailingIcon; + + Color defaultButtonColor(EnteColorScheme colorScheme) { + if (isPrimary) { + return colorScheme.primary400; + } + if (isSecondary) { + return colorScheme.fillFaint; + } + if (this == ButtonType.neutral || this == ButtonType.trailingIcon) { + return colorScheme.fillBase; + } + if (this == ButtonType.critical) { + return colorScheme.warning700; + } + if (this == ButtonType.tertiaryCritical) { + return Colors.transparent; + } + return Colors.transparent; + } + + //Returning null to fallback to default color + Color? pressedButtonColor(EnteColorScheme colorScheme) { + if (isPrimary) { + return colorScheme.primary700; + } + if (isSecondary) { + return colorScheme.fillFaintPressed; + } + if (isNeutral) { + return colorScheme.fillBasePressed; + } + if (this == ButtonType.critical) { + return colorScheme.warning800; + } + return null; + } + + //Returning null to fallback to default color + Color? disabledButtonColor( + EnteColorScheme colorScheme, + ButtonSize buttonSize, + ) { + if (buttonSize == ButtonSize.small && + (this == ButtonType.primary || + this == ButtonType.neutral || + this == ButtonType.critical)) { + return colorScheme.fillMuted; + } + if (isPrimary || this == ButtonType.critical || isNeutral) { + return colorScheme.fillFaint; + } + return null; + } + + Color defaultBorderColor(EnteColorScheme colorScheme, ButtonSize buttonSize) { + if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) { + return colorScheme.warning700; + } + return Colors.transparent; + } + + //Returning null to fallback to default color + Color? pressedBorderColor({ + required EnteColorScheme colorScheme, + required ButtonSize buttonSize, + }) { + if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) { + return colorScheme.warning700; + } + return null; + } + + //Returning null to fallback to default color + Color? disabledBorderColor( + EnteColorScheme colorScheme, + ButtonSize buttonSize, + ) { + if (this == ButtonType.tertiaryCritical && buttonSize == ButtonSize.large) { + return colorScheme.strokeMuted; + } + return null; + } + + Color defaultIconColor({ + required EnteColorScheme colorScheme, + required EnteColorScheme inverseColorScheme, + }) { + if (isPrimary || this == ButtonType.critical) { + return strokeBaseDark; + } + if (this == ButtonType.neutral || this == ButtonType.trailingIcon) { + return inverseColorScheme.strokeBase; + } + if (this == ButtonType.tertiaryCritical) { + return colorScheme.warning500; + } + //fallback + return colorScheme.strokeBase; + } + + //Returning null to fallback to default color + Color? pressedIconColor(EnteColorScheme colorScheme, ButtonSize buttonSize) { + if (this == ButtonType.tertiaryCritical) { + return colorScheme.warning700; + } + if (this == ButtonType.tertiary && buttonSize == ButtonSize.small) { + return colorScheme.fillBasePressed; + } + return null; + } + + //Returning null to fallback to default color + Color? disabledIconColor(EnteColorScheme colorScheme, ButtonSize buttonSize) { + if (isPrimary || + isSecondary || + isNeutral || + buttonSize == ButtonSize.small) { + return colorScheme.strokeMuted; + } + if (isCritical) { + return colorScheme.strokeFaint; + } + return null; + } + + TextStyle defaultLabelStyle({ + required EnteTextTheme textTheme, + required EnteTextTheme inverseTextTheme, + }) { + if (isPrimary || this == ButtonType.critical) { + return textTheme.bodyBold.copyWith(color: textBaseDark); + } + if (this == ButtonType.neutral || this == ButtonType.trailingIcon) { + return inverseTextTheme.bodyBold; + } + if (this == ButtonType.tertiaryCritical) { + return textTheme.bodyBold.copyWith(color: warning500); + } + //fallback + return textTheme.bodyBold; + } + + //Returning null to fallback to default color + TextStyle? pressedLabelStyle( + EnteTextTheme textTheme, + EnteColorScheme colorScheme, + ButtonSize buttonSize, + ) { + if (this == ButtonType.tertiaryCritical) { + return textTheme.bodyBold.copyWith(color: colorScheme.warning700); + } + if (this == ButtonType.tertiary && buttonSize == ButtonSize.small) { + return textTheme.bodyBold.copyWith(color: colorScheme.fillBasePressed); + } + return null; + } + + //Returning null to fallback to default color + TextStyle? disabledLabelStyle( + EnteTextTheme textTheme, + EnteColorScheme colorScheme, + ) { + return textTheme.bodyBold.copyWith(color: colorScheme.textFaint); + } + + //Returning null to fallback to default color + Color? checkIconColor(EnteColorScheme colorScheme) { + if (isSecondary) { + return colorScheme.primary500; + } + return null; + } +} diff --git a/mobile/packages/ui/lib/components/buttons/models/custom_button_style.dart b/mobile/packages/ui/lib/components/buttons/models/custom_button_style.dart new file mode 100644 index 0000000000..e30504ab3b --- /dev/null +++ b/mobile/packages/ui/lib/components/buttons/models/custom_button_style.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +class CustomButtonStyle { + Color defaultButtonColor; + Color? pressedButtonColor; + Color? disabledButtonColor; + Color defaultBorderColor; + Color? pressedBorderColor; + Color? disabledBorderColor; + Color defaultIconColor; + Color? pressedIconColor; + Color? disabledIconColor; + TextStyle defaultLabelStyle; + TextStyle? pressedLabelStyle; + TextStyle? disabledLabelStyle; + Color? checkIconColor; + + CustomButtonStyle({ + required this.defaultButtonColor, + this.pressedButtonColor, + this.disabledButtonColor, + required this.defaultBorderColor, + this.pressedBorderColor, + this.disabledBorderColor, + required this.defaultIconColor, + this.pressedIconColor, + this.disabledIconColor, + required this.defaultLabelStyle, + this.pressedLabelStyle, + this.disabledLabelStyle, + this.checkIconColor, + }); +} diff --git a/mobile/packages/ui/lib/components/captioned_text_widget.dart b/mobile/packages/ui/lib/components/captioned_text_widget.dart new file mode 100644 index 0000000000..f4d6eb423a --- /dev/null +++ b/mobile/packages/ui/lib/components/captioned_text_widget.dart @@ -0,0 +1,57 @@ +import '../theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class CaptionedTextWidget extends StatelessWidget { + final String title; + final String? subTitle; + final TextStyle? textStyle; + final bool makeTextBold; + final Color? textColor; + const CaptionedTextWidget({ + required this.title, + this.subTitle, + this.textStyle, + this.makeTextBold = false, + this.textColor, + super.key, + }); + + @override + Widget build(BuildContext context) { + final enteColorScheme = getEnteColorScheme(context); + final enteTextTheme = getEnteTextTheme(context); + + return Flexible( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 2), + child: Row( + children: [ + Flexible( + child: RichText( + text: TextSpan( + style: textStyle ?? + (makeTextBold + ? enteTextTheme.bodyBold.copyWith(color: textColor) + : enteTextTheme.body.copyWith(color: textColor)), + children: [ + TextSpan( + text: title, + ), + subTitle != null + ? TextSpan( + text: ' \u2022 $subTitle', + style: enteTextTheme.small.copyWith( + color: enteColorScheme.textMuted, + ), + ) + : const TextSpan(text: ''), + ], + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/components_constants.dart b/mobile/packages/ui/lib/components/components_constants.dart new file mode 100644 index 0000000000..2f8cbf52b1 --- /dev/null +++ b/mobile/packages/ui/lib/components/components_constants.dart @@ -0,0 +1,4 @@ +const double mobileSmallThreshold = 336; + +//Screen width of iPhone 14 pro max in points is taken as maximum +const double restrictedMaxWidth = 430; diff --git a/mobile/packages/ui/lib/components/developer_settings_widget.dart b/mobile/packages/ui/lib/components/developer_settings_widget.dart new file mode 100644 index 0000000000..408c4d7111 --- /dev/null +++ b/mobile/packages/ui/lib/components/developer_settings_widget.dart @@ -0,0 +1,32 @@ +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_configuration/constants.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; + +class DeveloperSettingsWidget extends StatelessWidget { + final BaseConfiguration configuration; + + const DeveloperSettingsWidget( + this.configuration, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final endpoint = configuration.getHttpEndpoint(); + if (endpoint != kDefaultProductionEndpoint) { + final endpointURI = Uri.parse(endpoint); + return Padding( + padding: const EdgeInsets.only(bottom: 20), + child: Text( + context.strings.customEndpoint( + "${endpointURI.host}:${endpointURI.port}", + ), + style: Theme.of(context).textTheme.bodySmall, + ), + ); + } else { + return const SizedBox.shrink(); + } + } +} diff --git a/mobile/packages/ui/lib/components/dialog_widget.dart b/mobile/packages/ui/lib/components/dialog_widget.dart new file mode 100644 index 0000000000..53be98c476 --- /dev/null +++ b/mobile/packages/ui/lib/components/dialog_widget.dart @@ -0,0 +1,291 @@ +import 'dart:math'; + +import 'package:ente_base/typedefs.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_result.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/components_constants.dart'; +import 'package:ente_ui/components/separators.dart'; +import 'package:ente_ui/components/text_input_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +///Will return null if dismissed by tapping outside +Future showDialogWidget({ + required BuildContext context, + required String title, + String? body, + required List buttons, + IconData? icon, + bool isDismissible = true, + bool useRootNavigator = false, +}) { + return showDialog( + useRootNavigator: useRootNavigator, + barrierDismissible: isDismissible, + barrierColor: getEnteColorScheme(context).backdropFaint, + context: context, + builder: (context) { + final widthOfScreen = MediaQuery.of(context).size.width; + final isMobileSmall = widthOfScreen <= mobileSmallThreshold; + return Padding( + padding: EdgeInsets.symmetric(horizontal: isMobileSmall ? 8 : 0), + child: Dialog( + insetPadding: EdgeInsets.zero, + child: DialogWidget( + title: title, + body: body, + buttons: buttons, + icon: icon, + ), + ), + ); + }, + ); +} + +class DialogWidget extends StatelessWidget { + final String title; + final String? body; + final List buttons; + final IconData? icon; + const DialogWidget({ + required this.title, + this.body, + required this.buttons, + this.icon, + super.key, + }); + + @override + Widget build(BuildContext context) { + final widthOfScreen = MediaQuery.of(context).size.width; + final isMobileSmall = widthOfScreen <= mobileSmallThreshold; + final colorScheme = getEnteColorScheme(context); + return Container( + width: min(widthOfScreen, 320), + padding: isMobileSmall + ? const EdgeInsets.all(0) + : const EdgeInsets.fromLTRB(6, 8, 6, 6), + decoration: BoxDecoration( + color: colorScheme.backgroundElevated, + boxShadow: getEnteShadowFloat(context), + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + child: Material( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.all(16), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ContentContainer( + title: title, + body: body, + icon: icon, + ), + const SizedBox(height: 36), + Actions(buttons), + ], + ), + ), + ), + ), + ); + } +} + +class ContentContainer extends StatelessWidget { + final String title; + final String? body; + final IconData? icon; + const ContentContainer({ + required this.title, + this.body, + this.icon, + super.key, + }); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorScheme = getEnteColorScheme(context); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + icon == null + ? const SizedBox.shrink() + : Row( + children: [ + Icon( + icon, + size: 32, + ), + ], + ), + icon == null ? const SizedBox.shrink() : const SizedBox(height: 19), + Text(title, style: textTheme.largeBold), + body != null ? const SizedBox(height: 19) : const SizedBox.shrink(), + body != null + ? Text( + body!, + style: textTheme.body.copyWith(color: colorScheme.textMuted), + ) + : const SizedBox.shrink(), + ], + ); + } +} + +class Actions extends StatelessWidget { + final List buttons; + const Actions(this.buttons, {super.key}); + + @override + Widget build(BuildContext context) { + return Column( + children: addSeparators( + buttons, + const SizedBox( + // In figma this white space is of height 8pts. But the Button + // component has 1pts of invisible border by default in code. So two + // 1pts borders will visually make the whitespace 8pts. + // Height of button component in figma = 48, in code = 50 (2pts for + // top + bottom border) + height: 6, + ), + ), + ); + } +} + +class TextInputDialog extends StatefulWidget { + final String title; + final String? body; + final String submitButtonLabel; + final IconData? icon; + final String? label; + final String? message; + final FutureVoidCallbackParamStr onSubmit; + final String? hintText; + final IconData? prefixIcon; + final String? initialValue; + final Alignment? alignMessage; + final int? maxLength; + final bool showOnlyLoadingState; + final TextCapitalization? textCapitalization; + final bool alwaysShowSuccessState; + final bool isPasswordInput; + const TextInputDialog({ + required this.title, + this.body, + required this.submitButtonLabel, + required this.onSubmit, + this.icon, + this.label, + this.message, + this.hintText, + this.prefixIcon, + this.initialValue, + this.alignMessage, + this.maxLength, + this.textCapitalization, + this.showOnlyLoadingState = false, + this.alwaysShowSuccessState = false, + this.isPasswordInput = false, + super.key, + }); + + @override + State createState() => _TextInputDialogState(); +} + +class _TextInputDialogState extends State { + //the value of this ValueNotifier has no significance + final _submitNotifier = ValueNotifier(false); + + @override + void dispose() { + _submitNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final widthOfScreen = MediaQuery.of(context).size.width; + final isMobileSmall = widthOfScreen <= mobileSmallThreshold; + final colorScheme = getEnteColorScheme(context); + return Container( + width: min(widthOfScreen, 320), + padding: isMobileSmall + ? const EdgeInsets.all(0) + : const EdgeInsets.fromLTRB(6, 8, 6, 6), + decoration: BoxDecoration( + color: colorScheme.backgroundElevated, + boxShadow: getEnteShadowFloat(context), + borderRadius: const BorderRadius.all(Radius.circular(8)), + ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ContentContainer( + title: widget.title, + body: widget.body, + icon: widget.icon, + ), + Padding( + padding: const EdgeInsets.only(top: 19), + child: TextInputWidget( + label: widget.label, + message: widget.message, + hintText: widget.hintText, + prefixIcon: widget.prefixIcon, + initialValue: widget.initialValue, + alignMessage: widget.alignMessage, + autoFocus: true, + maxLength: widget.maxLength, + submitNotifier: _submitNotifier, + onSubmit: widget.onSubmit, + popNavAfterSubmission: true, + showOnlyLoadingState: widget.showOnlyLoadingState, + textCapitalization: widget.textCapitalization, + alwaysShowSuccessState: widget.alwaysShowSuccessState, + isPasswordInput: widget.isPasswordInput, + ), + ), + const SizedBox(height: 36), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: ButtonWidget( + buttonType: ButtonType.secondary, + buttonSize: ButtonSize.small, + labelText: context.strings.cancel, + isInAlert: true, + ), + ), + const SizedBox(width: 8), + Expanded( + child: ButtonWidget( + buttonSize: ButtonSize.small, + buttonType: ButtonType.neutral, + labelText: widget.submitButtonLabel, + onTap: () async { + _submitNotifier.value = !_submitNotifier.value; + }, + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/dialogs.dart b/mobile/packages/ui/lib/components/dialogs.dart new file mode 100644 index 0000000000..1bb7071eef --- /dev/null +++ b/mobile/packages/ui/lib/components/dialogs.dart @@ -0,0 +1,124 @@ +import 'package:ente_base/typedefs.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_result.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/dialog_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +enum DialogUserChoice { firstChoice, secondChoice } + +enum ActionType { + confirm, + critical, +} + +// if dialog is dismissed by tapping outside, this will return null +Future showChoiceDialogOld( + BuildContext context, + String title, + String content, { + String firstAction = 'Ok', + Color? firstActionColor, + String secondAction = 'Cancel', + Color? secondActionColor, + ActionType actionType = ActionType.confirm, +}) { + final AlertDialog alert = AlertDialog( + title: Text( + title, + style: TextStyle( + color: actionType == ActionType.critical + ? Colors.red + : getEnteColorScheme(context).primary500, + ), + ), + content: Text( + content, + style: const TextStyle( + height: 1.4, + ), + ), + actions: [ + TextButton( + child: Text( + firstAction, + style: TextStyle( + color: firstActionColor ?? + (actionType == ActionType.critical + ? Colors.red + : getEnteColorScheme(context).surface), + ), + ), + onPressed: () { + Navigator.of(context, rootNavigator: true) + .pop(DialogUserChoice.firstChoice); + }, + ), + TextButton( + child: Text( + secondAction, + style: TextStyle( + color: secondActionColor ?? + getEnteColorScheme(context).alternativeColor, + ), + ), + onPressed: () { + Navigator.of(context, rootNavigator: true) + .pop(DialogUserChoice.secondChoice); + }, + ), + ], + ); + + return showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + barrierColor: Colors.black87, + ); +} + +///Will return null if dismissed by tapping outside +Future showChoiceDialog( + BuildContext context, { + required String title, + String? body, + required String firstButtonLabel, + String secondButtonLabel = "Cancel", + ButtonType firstButtonType = ButtonType.neutral, + ButtonType secondButtonType = ButtonType.secondary, + ButtonAction firstButtonAction = ButtonAction.first, + ButtonAction secondButtonAction = ButtonAction.cancel, + FutureVoidCallback? firstButtonOnTap, + FutureVoidCallback? secondButtonOnTap, + bool isCritical = false, + IconData? icon, + bool isDismissible = true, +}) async { + final buttons = [ + ButtonWidget( + buttonType: isCritical ? ButtonType.critical : firstButtonType, + labelText: firstButtonLabel, + isInAlert: true, + onTap: firstButtonOnTap, + buttonAction: firstButtonAction, + ), + ButtonWidget( + buttonType: secondButtonType, + labelText: secondButtonLabel, + isInAlert: true, + onTap: secondButtonOnTap, + buttonAction: secondButtonAction, + ), + ]; + return showDialogWidget( + context: context, + title: title, + body: body, + buttons: buttons, + icon: icon, + isDismissible: isDismissible, + ); +} diff --git a/mobile/packages/ui/lib/components/divider_widget.dart b/mobile/packages/ui/lib/components/divider_widget.dart new file mode 100644 index 0000000000..c4a3374abb --- /dev/null +++ b/mobile/packages/ui/lib/components/divider_widget.dart @@ -0,0 +1,69 @@ +import '../theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +enum DividerType { + solid, + menu, + menuNoIcon, + bottomBar, +} + +class DividerWidget extends StatelessWidget { + final DividerType dividerType; + final Color bgColor; + final bool divColorHasBlur; + final EdgeInsets? padding; + const DividerWidget({ + super.key, + required this.dividerType, + this.bgColor = Colors.transparent, + this.divColorHasBlur = true, + this.padding, + }); + + @override + Widget build(BuildContext context) { + final dividerColor = divColorHasBlur + ? getEnteColorScheme(context).blurStrokeFaint + : getEnteColorScheme(context).strokeFaint; + + if (dividerType == DividerType.solid) { + return Container( + color: getEnteColorScheme(context).strokeFaint, + width: double.infinity, + height: 1, + ); + } + if (dividerType == DividerType.bottomBar) { + return Container( + color: dividerColor, + width: double.infinity, + height: 1, + ); + } + + return Container( + color: bgColor, + padding: padding ?? EdgeInsets.zero, + child: Row( + children: [ + SizedBox( + width: dividerType == DividerType.menu + ? 48 + : dividerType == DividerType.menuNoIcon + ? 16 + : 0, + height: 1, + ), + Expanded( + child: Container( + color: dividerColor, + height: 1, + width: double.infinity, + ), + ), + ], + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/loading_widget.dart b/mobile/packages/ui/lib/components/loading_widget.dart new file mode 100644 index 0000000000..999f0aa3c2 --- /dev/null +++ b/mobile/packages/ui/lib/components/loading_widget.dart @@ -0,0 +1,33 @@ +import '../theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class EnteLoadingWidget extends StatelessWidget { + final Color? color; + final double size; + final double padding; + final Alignment alignment; + const EnteLoadingWidget({ + this.color, + this.size = 14, + this.padding = 5, + this.alignment = Alignment.center, + super.key, + }); + + @override + Widget build(BuildContext context) { + return Align( + alignment: alignment, + child: Padding( + padding: EdgeInsets.all(padding), + child: SizedBox.fromSize( + size: Size.square(size), + child: CircularProgressIndicator( + strokeWidth: 2, + color: color ?? getEnteColorScheme(context).strokeBase, + ), + ), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/menu_item_child_widgets.dart b/mobile/packages/ui/lib/components/menu_item_child_widgets.dart new file mode 100644 index 0000000000..67b2937ed0 --- /dev/null +++ b/mobile/packages/ui/lib/components/menu_item_child_widgets.dart @@ -0,0 +1,173 @@ +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/models/execution_states.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class TrailingWidget extends StatefulWidget { + final ValueNotifier executionStateNotifier; + final IconData? trailingIcon; + final Color? trailingIconColor; + final Widget? trailingWidget; + final bool trailingIconIsMuted; + final double trailingExtraMargin; + final bool showExecutionStates; + const TrailingWidget({ + super.key, + required this.executionStateNotifier, + this.trailingIcon, + this.trailingIconColor, + this.trailingWidget, + required this.trailingIconIsMuted, + required this.trailingExtraMargin, + required this.showExecutionStates, + }); + @override + State createState() => _TrailingWidgetState(); +} + +class _TrailingWidgetState extends State { + Widget? trailingWidget; + @override + void initState() { + widget.showExecutionStates + ? widget.executionStateNotifier.addListener(_executionStateListener) + : null; + super.initState(); + } + + @override + void dispose() { + widget.executionStateNotifier.removeListener(_executionStateListener); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (trailingWidget == null || !widget.showExecutionStates) { + _setTrailingIcon(); + } + return AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: trailingWidget, + ); + } + + void _executionStateListener() { + final colorScheme = getEnteColorScheme(context); + setState(() { + if (widget.executionStateNotifier.value == ExecutionState.idle) { + _setTrailingIcon(); + } else if (widget.executionStateNotifier.value == + ExecutionState.inProgress) { + trailingWidget = EnteLoadingWidget( + color: colorScheme.strokeMuted, + ); + } else if (widget.executionStateNotifier.value == + ExecutionState.successful) { + trailingWidget = Icon( + Icons.check_outlined, + size: 22, + color: colorScheme.primary500, + ); + } else { + trailingWidget = const SizedBox.shrink(); + } + }); + } + + void _setTrailingIcon() { + if (widget.trailingIcon != null) { + trailingWidget = Padding( + padding: EdgeInsets.only( + right: widget.trailingExtraMargin, + ), + child: Icon( + widget.trailingIcon, + color: widget.trailingIconIsMuted + ? getEnteColorScheme(context).strokeMuted + : widget.trailingIconColor, + ), + ); + } else { + trailingWidget = widget.trailingWidget ?? const SizedBox.shrink(); + } + } +} + +class ExpansionTrailingIcon extends StatelessWidget { + final bool isExpanded; + final IconData? trailingIcon; + final Color? trailingIconColor; + const ExpansionTrailingIcon({ + super.key, + required this.isExpanded, + this.trailingIcon, + this.trailingIconColor, + }); + + @override + Widget build(BuildContext context) { + return AnimatedOpacity( + duration: const Duration(milliseconds: 100), + curve: Curves.easeInOut, + opacity: isExpanded ? 0 : 1, + child: AnimatedSwitcher( + transitionBuilder: (child, animation) { + return ScaleTransition(scale: animation, child: child); + }, + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeOut, + child: isExpanded + ? const SizedBox.shrink() + : Icon( + trailingIcon, + color: trailingIconColor, + ), + ), + ); + } +} + +class LeadingWidget extends StatelessWidget { + final IconData? leadingIcon; + final Color? leadingIconColor; + + final Widget? leadingIconWidget; + // leadIconSize deafult value is 20. + final double leadingIconSize; + const LeadingWidget({ + super.key, + required this.leadingIconSize, + this.leadingIcon, + this.leadingIconColor, + this.leadingIconWidget, + }); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(right: 10), + child: SizedBox( + height: leadingIconSize, + width: leadingIconSize, + child: leadingIcon == null + ? (leadingIconWidget != null + ? FittedBox( + fit: BoxFit.contain, + child: leadingIconWidget, + ) + : const SizedBox.shrink()) + : FittedBox( + fit: BoxFit.contain, + child: Icon( + leadingIcon, + color: leadingIconColor ?? + getEnteColorScheme(context).strokeBase, + ), + ), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/menu_item_widget.dart b/mobile/packages/ui/lib/components/menu_item_widget.dart new file mode 100644 index 0000000000..1c1d97ec8e --- /dev/null +++ b/mobile/packages/ui/lib/components/menu_item_widget.dart @@ -0,0 +1,291 @@ +import 'package:ente_base/typedefs.dart'; +import 'package:ente_ui/components/menu_item_child_widgets.dart'; +import 'package:ente_ui/models/execution_states.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/debouncer.dart'; +import 'package:flutter/material.dart'; + +class MenuItemWidget extends StatefulWidget { + final Widget captionedTextWidget; + final bool isExpandable; + + /// leading icon can be passed without specifing size of icon, + /// this component sets size to 20x20 irrespective of passed icon's size + final IconData? leadingIcon; + final Color? leadingIconColor; + + final Widget? leadingIconWidget; + + // leadIconSize deafult value is 20. + final double leadingIconSize; + + /// trailing icon can be passed without size as default size set by + /// flutter is what this component expects + final IconData? trailingIcon; + final Color? trailingIconColor; + final Widget? trailingWidget; + final bool trailingIconIsMuted; + + /// If provided, add this much extra spacing to the right of the trailing icon. + final double trailingExtraMargin; + final FutureVoidCallback? onTap; + final VoidCallback? onDoubleTap; + final Color? menuItemColor; + final bool alignCaptionedTextToLeft; + + // singleBorderRadius is applied to the border when it's a standalone menu item. + // Widget will apply singleBorderRadius if value of both isTopBorderRadiusRemoved + // and isBottomBorderRadiusRemoved is false. Otherwise, multipleBorderRadius will + // be applied + final double singleBorderRadius; + final double multipleBorderRadius; + final Color? pressedColor; + final ExpansibleController? expandableController; + final bool isBottomBorderRadiusRemoved; + final bool isTopBorderRadiusRemoved; + + /// disable gesture detector if not used + final bool isGestureDetectorDisabled; + + ///Success state will not be shown if this flag is set to true, only idle and + ///loading state + final bool showOnlyLoadingState; + + final bool surfaceExecutionStates; + + ///To show success state even when execution time < debouce time, set this + ///flag to true. If the loading state needs to be shown and success state not, + ///set the showOnlyLoadingState flag to true, setting this flag to false won't + ///help. + final bool alwaysShowSuccessState; + + const MenuItemWidget({ + required this.captionedTextWidget, + this.isExpandable = false, + this.leadingIcon, + this.leadingIconColor, + this.leadingIconSize = 20.0, + this.leadingIconWidget, + this.trailingIcon, + this.trailingIconColor, + this.trailingWidget, + this.trailingIconIsMuted = false, + this.trailingExtraMargin = 0.0, + this.onTap, + this.onDoubleTap, + this.menuItemColor, + this.alignCaptionedTextToLeft = false, + this.singleBorderRadius = 4.0, + this.multipleBorderRadius = 8.0, + this.pressedColor, + this.expandableController, + this.isBottomBorderRadiusRemoved = false, + this.isTopBorderRadiusRemoved = false, + this.isGestureDetectorDisabled = false, + this.showOnlyLoadingState = false, + this.surfaceExecutionStates = true, + this.alwaysShowSuccessState = false, + super.key, + }); + + @override + State createState() => _MenuItemWidgetState(); +} + +class _MenuItemWidgetState extends State { + final _debouncer = Debouncer(const Duration(milliseconds: 300)); + ValueNotifier executionStateNotifier = + ValueNotifier(ExecutionState.idle); + + Color? menuItemColor; + late double borderRadius; + + @override + void initState() { + menuItemColor = widget.menuItemColor; + borderRadius = + (widget.isBottomBorderRadiusRemoved || widget.isTopBorderRadiusRemoved) + ? widget.multipleBorderRadius + : widget.singleBorderRadius; + if (widget.expandableController != null) { + widget.expandableController!.addListener(() { + setState(() {}); + }); + } + super.initState(); + } + + @override + void didChangeDependencies() { + menuItemColor = widget.menuItemColor; + super.didChangeDependencies(); + } + + @override + void didUpdateWidget(covariant MenuItemWidget oldWidget) { + menuItemColor = widget.menuItemColor; + super.didUpdateWidget(oldWidget); + } + + @override + void dispose() { + if (widget.expandableController != null) { + widget.expandableController!.dispose(); + } + executionStateNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return widget.isExpandable || widget.isGestureDetectorDisabled + ? menuItemWidget(context) + : GestureDetector( + onTap: _onTap, + onDoubleTap: widget.onDoubleTap, + onTapDown: _onTapDown, + onTapUp: _onTapUp, + onTapCancel: _onCancel, + child: menuItemWidget(context), + ); + } + + Widget menuItemWidget(BuildContext context) { + final circularRadius = Radius.circular(borderRadius); + final isExpanded = widget.expandableController?.isExpanded; + final bottomBorderRadius = + (isExpanded != null && isExpanded) || widget.isBottomBorderRadiusRemoved + ? const Radius.circular(0) + : circularRadius; + final topBorderRadius = widget.isTopBorderRadiusRemoved + ? const Radius.circular(0) + : circularRadius; + return AnimatedContainer( + duration: const Duration(milliseconds: 20), + width: double.infinity, + padding: const EdgeInsets.only(left: 16, right: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: topBorderRadius, + topRight: topBorderRadius, + bottomLeft: bottomBorderRadius, + bottomRight: bottomBorderRadius, + ), + color: menuItemColor, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + widget.alignCaptionedTextToLeft && widget.leadingIcon == null + ? const SizedBox.shrink() + : LeadingWidget( + leadingIconSize: widget.leadingIconSize, + leadingIcon: widget.leadingIcon, + leadingIconColor: widget.leadingIconColor, + leadingIconWidget: widget.leadingIconWidget, + ), + widget.captionedTextWidget, + if (widget.expandableController != null) + ExpansionTrailingIcon( + isExpanded: isExpanded!, + trailingIcon: widget.trailingIcon, + trailingIconColor: widget.trailingIconColor, + ) + else + TrailingWidget( + executionStateNotifier: executionStateNotifier, + trailingIcon: widget.trailingIcon, + trailingIconColor: widget.trailingIconColor, + trailingWidget: widget.trailingWidget, + trailingIconIsMuted: widget.trailingIconIsMuted, + trailingExtraMargin: widget.trailingExtraMargin, + showExecutionStates: widget.surfaceExecutionStates, + key: ValueKey(widget.trailingIcon.hashCode), + ), + ], + ), + ); + } + + Future _onTap() async { + if (executionStateNotifier.value == ExecutionState.inProgress || + executionStateNotifier.value == ExecutionState.successful) { + return; + } + _debouncer.run( + () => Future( + () { + executionStateNotifier.value = ExecutionState.inProgress; + }, + ), + ); + await widget.onTap?.call().then( + (value) { + widget.alwaysShowSuccessState + ? executionStateNotifier.value = ExecutionState.successful + : null; + }, + onError: (error, stackTrace) => _debouncer.cancelDebounce(), + ); + _debouncer.cancelDebounce(); + if (widget.alwaysShowSuccessState) { + Future.delayed(const Duration(seconds: 2), () { + executionStateNotifier.value = ExecutionState.idle; + }); + return; + } + if (executionStateNotifier.value == ExecutionState.inProgress) { + if (widget.showOnlyLoadingState) { + executionStateNotifier.value = ExecutionState.idle; + } else { + executionStateNotifier.value = ExecutionState.successful; + Future.delayed(const Duration(seconds: 2), () { + executionStateNotifier.value = ExecutionState.idle; + }); + } + } + } + + void _onTapDown(details) { + if (executionStateNotifier.value == ExecutionState.inProgress || + executionStateNotifier.value == ExecutionState.successful) { + return; + } + setState(() { + if (widget.pressedColor == null) { + hasPassedGestureCallbacks() + ? menuItemColor = getEnteColorScheme(context).fillFaintPressed + : menuItemColor = widget.menuItemColor; + } else { + menuItemColor = widget.pressedColor; + } + }); + } + + bool hasPassedGestureCallbacks() { + return widget.onDoubleTap != null || widget.onTap != null; + } + + void _onTapUp(details) { + if (executionStateNotifier.value == ExecutionState.inProgress || + executionStateNotifier.value == ExecutionState.successful) { + return; + } + Future.delayed( + const Duration(milliseconds: 100), + () => setState(() { + menuItemColor = widget.menuItemColor; + }), + ); + } + + void _onCancel() { + if (executionStateNotifier.value == ExecutionState.inProgress || + executionStateNotifier.value == ExecutionState.successful) { + return; + } + setState(() { + menuItemColor = widget.menuItemColor; + }); + } +} diff --git a/mobile/packages/ui/lib/components/progress_dialog.dart b/mobile/packages/ui/lib/components/progress_dialog.dart new file mode 100644 index 0000000000..adabff2ee0 --- /dev/null +++ b/mobile/packages/ui/lib/components/progress_dialog.dart @@ -0,0 +1,289 @@ +import 'package:flutter/material.dart'; + +enum ProgressDialogType { normal, download } + +String _dialogMessage = "Loading..."; +double _progress = 0.0, _maxProgress = 100.0; + +Widget? _customBody; + +TextAlign _textAlign = TextAlign.left; +Alignment _progressWidgetAlignment = Alignment.centerLeft; + +TextDirection _direction = TextDirection.ltr; + +bool _isShowing = false; +BuildContext? _context, _dismissingContext; +ProgressDialogType? _progressDialogType; +bool _barrierDismissible = true, _showLogs = false; +Color? _barrierColor; + +TextStyle _progressTextStyle = const TextStyle( + color: Colors.black, + fontSize: 12.0, + fontWeight: FontWeight.w400, + ), + _messageStyle = const TextStyle( + color: Colors.black, + fontSize: 18.0, + fontWeight: FontWeight.w600, + ); + +double _dialogElevation = 8.0, _borderRadius = 8.0; +Color _backgroundColor = Colors.white; +Curve _insetAnimCurve = Curves.easeInOut; +EdgeInsets _dialogPadding = const EdgeInsets.all(8.0); + +Widget _progressWidget = Image.asset( + 'assets/double_ring_loading_io.gif', + package: 'progress_dialog', +); + +class ProgressDialog { + _Body? _dialog; + + ProgressDialog( + BuildContext context, { + ProgressDialogType? type, + bool? isDismissible, + bool? showLogs, + TextDirection? textDirection, + Widget? customBody, + Color? barrierColor, + }) { + _context = context; + _progressDialogType = type ?? ProgressDialogType.normal; + _barrierDismissible = isDismissible ?? true; + _showLogs = showLogs ?? false; + _customBody = customBody; + _direction = textDirection ?? TextDirection.ltr; + _barrierColor = barrierColor ?? barrierColor; + } + + void style({ + Widget? child, + double? progress, + double? maxProgress, + String? message, + Widget? progressWidget, + Color? backgroundColor, + TextStyle? progressTextStyle, + TextStyle? messageTextStyle, + double? elevation, + TextAlign? textAlign, + double? borderRadius, + Curve? insetAnimCurve, + EdgeInsets? padding, + Alignment? progressWidgetAlignment, + }) { + if (_isShowing) return; + if (_progressDialogType == ProgressDialogType.download) { + _progress = progress ?? _progress; + } + + _dialogMessage = message ?? _dialogMessage; + _maxProgress = maxProgress ?? _maxProgress; + _progressWidget = progressWidget ?? _progressWidget; + _backgroundColor = backgroundColor ?? _backgroundColor; + _messageStyle = messageTextStyle ?? _messageStyle; + _progressTextStyle = progressTextStyle ?? _progressTextStyle; + _dialogElevation = elevation ?? _dialogElevation; + _borderRadius = borderRadius ?? _borderRadius; + _insetAnimCurve = insetAnimCurve ?? _insetAnimCurve; + _textAlign = textAlign ?? _textAlign; + _progressWidget = child ?? _progressWidget; + _dialogPadding = padding ?? _dialogPadding; + _progressWidgetAlignment = + progressWidgetAlignment ?? _progressWidgetAlignment; + } + + void update({ + double? progress, + double? maxProgress, + String? message, + Widget? progressWidget, + TextStyle? progressTextStyle, + TextStyle? messageTextStyle, + }) { + if (_progressDialogType == ProgressDialogType.download) { + _progress = progress ?? _progress; + } + + _dialogMessage = message ?? _dialogMessage; + _maxProgress = maxProgress ?? _maxProgress; + _progressWidget = progressWidget ?? _progressWidget; + _messageStyle = messageTextStyle ?? _messageStyle; + _progressTextStyle = progressTextStyle ?? _progressTextStyle; + + if (_isShowing) _dialog!.update(); + } + + bool isShowing() { + return _isShowing; + } + + Future hide() async { + try { + if (_isShowing) { + _isShowing = false; + if (_dismissingContext != null) { + Navigator.of(_dismissingContext!).pop(); + } + if (_showLogs) debugPrint('ProgressDialog dismissed'); + return Future.value(true); + } else { + if (_showLogs) debugPrint('ProgressDialog already dismissed'); + return Future.value(false); + } + } catch (err) { + debugPrint('Seems there is an issue hiding dialog'); + debugPrint(err.toString()); + return Future.value(false); + } + } + + Future show() async { + try { + if (!_isShowing) { + _dialog = _Body(); + // ignore: unawaited_futures + showDialog( + context: _context!, + barrierDismissible: _barrierDismissible, + barrierColor: _barrierColor, + builder: (BuildContext context) { + _dismissingContext = context; + return PopScope( + canPop: _barrierDismissible, + child: Dialog( + backgroundColor: _backgroundColor, + insetAnimationCurve: _insetAnimCurve, + insetAnimationDuration: const Duration(milliseconds: 100), + elevation: _dialogElevation, + shape: RoundedRectangleBorder( + borderRadius: + BorderRadius.all(Radius.circular(_borderRadius)), + ), + child: _dialog, + ), + ); + }, + ); + // Delaying the function for 200 milliseconds + // [Default transitionDuration of DialogRoute] + await Future.delayed(const Duration(milliseconds: 200)); + if (_showLogs) debugPrint('ProgressDialog shown'); + _isShowing = true; + return true; + } else { + if (_showLogs) debugPrint("ProgressDialog already shown/showing"); + return false; + } + } catch (err) { + _isShowing = false; + debugPrint('Exception while showing the dialog'); + debugPrint(err.toString()); + return false; + } + } +} + +// ignore: must_be_immutable +class _Body extends StatefulWidget { + final _BodyState _dialog = _BodyState(); + + update() { + _dialog.update(); + } + + @override + State createState() { + // ignore: no_logic_in_create_state + return _dialog; + } +} + +class _BodyState extends State<_Body> { + update() { + setState(() {}); + } + + @override + void dispose() { + _isShowing = false; + if (_showLogs) debugPrint('ProgressDialog dismissed by back button'); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final loader = Align( + alignment: _progressWidgetAlignment, + child: SizedBox( + width: 60.0, + height: 60.0, + child: _progressWidget, + ), + ); + + final text = Expanded( + child: _progressDialogType == ProgressDialogType.normal + ? Text( + _dialogMessage, + textAlign: _textAlign, + style: _messageStyle, + textDirection: _direction, + ) + : Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(height: 8.0), + Row( + children: [ + Expanded( + child: Text( + _dialogMessage, + style: _messageStyle, + textDirection: _direction, + ), + ), + ], + ), + const SizedBox(height: 4.0), + Align( + alignment: Alignment.bottomRight, + child: Text( + "$_progress/$_maxProgress", + style: _progressTextStyle, + textDirection: _direction, + ), + ), + ], + ), + ), + ); + + return _customBody ?? + Container( + padding: _dialogPadding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // row body + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(width: 8.0), + _direction == TextDirection.ltr ? loader : text, + const SizedBox(width: 8.0), + _direction == TextDirection.rtl ? loader : text, + const SizedBox(width: 8.0), + ], + ), + ], + ), + ); + } +} diff --git a/mobile/packages/ui/lib/components/separators.dart b/mobile/packages/ui/lib/components/separators.dart new file mode 100644 index 0000000000..bc4b1346ad --- /dev/null +++ b/mobile/packages/ui/lib/components/separators.dart @@ -0,0 +1,13 @@ +//This method returns a newly declared list with separators. It will not +//modify the original list +import 'package:flutter/widgets.dart'; + +List addSeparators(List listOfWidgets, Widget separator) { + final int initialLength = listOfWidgets.length; + final listOfWidgetsWithSeparators = []; + listOfWidgetsWithSeparators.addAll(listOfWidgets); + for (var i = 1; i < initialLength; i++) { + listOfWidgetsWithSeparators.insert((2 * i) - 1, separator); + } + return listOfWidgetsWithSeparators; +} diff --git a/mobile/packages/ui/lib/components/text_input_widget.dart b/mobile/packages/ui/lib/components/text_input_widget.dart new file mode 100644 index 0000000000..b310f31a08 --- /dev/null +++ b/mobile/packages/ui/lib/components/text_input_widget.dart @@ -0,0 +1,406 @@ +import 'package:ente_base/typedefs.dart'; +import 'package:ente_logging/logging.dart'; +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/components/separators.dart'; +import 'package:ente_ui/models/execution_states.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/debouncer.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class TextInputWidget extends StatefulWidget { + final String? label; + final String? message; + final String? hintText; + final IconData? prefixIcon; + final String? initialValue; + final Alignment? alignMessage; + final bool? autoFocus; + final int? maxLength; + + ///TextInputWidget will listen to this notifier and executes onSubmit when + ///notified. + final ValueNotifier? submitNotifier; + final bool alwaysShowSuccessState; + final bool showOnlyLoadingState; + final FutureVoidCallbackParamStr? onSubmit; + final VoidCallbackParamStr? onChange; + final bool popNavAfterSubmission; + final bool shouldSurfaceExecutionStates; + final TextCapitalization? textCapitalization; + final bool isPasswordInput; + final bool cancellable; + final bool shouldUnfocusOnCancelOrSubmit; + const TextInputWidget({ + this.onSubmit, + this.onChange, + this.label, + this.message, + this.hintText, + this.prefixIcon, + this.initialValue, + this.alignMessage, + this.autoFocus, + this.maxLength, + this.submitNotifier, + 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, + super.key, + }); + + @override + State createState() => _TextInputWidgetState(); +} + +class _TextInputWidgetState extends State { + final _logger = Logger("TextInputWidget"); + ExecutionState executionState = ExecutionState.idle; + final _textController = TextEditingController(); + final _debouncer = Debouncer(const Duration(milliseconds: 300)); + late final ValueNotifier _obscureTextNotifier; + + ///This is to pass if the TextInputWidget is in a dialog and an error is + ///thrown in executing onSubmit by passing it as arg in Navigator.pop() + Exception? _exception; + bool _incorrectPassword = false; + @override + void initState() { + widget.submitNotifier?.addListener(_onSubmit); + + if (widget.initialValue != null) { + _textController.value = TextEditingValue( + text: widget.initialValue!, + selection: TextSelection.collapsed(offset: widget.initialValue!.length), + ); + } + if (widget.onChange != null) { + _textController.addListener(() { + widget.onChange!.call(_textController.text); + }); + } + _obscureTextNotifier = ValueNotifier(widget.isPasswordInput); + _obscureTextNotifier.addListener(_safeRefresh); + super.initState(); + } + + @override + void dispose() { + widget.submitNotifier?.removeListener(_onSubmit); + _obscureTextNotifier.dispose(); + _textController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (executionState == ExecutionState.successful) { + Future.delayed(Duration(seconds: widget.popNavAfterSubmission ? 1 : 2), + () { + setState(() { + executionState = ExecutionState.idle; + }); + }); + } + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + var textInputChildren = []; + if (widget.label != null) { + textInputChildren.add(Text(widget.label!)); + } + textInputChildren.add( + ClipRRect( + borderRadius: const BorderRadius.all(Radius.circular(8)), + child: Material( + child: TextFormField( + textCapitalization: widget.textCapitalization!, + autofocus: widget.autoFocus ?? false, + controller: _textController, + inputFormatters: widget.maxLength != null + ? [LengthLimitingTextInputFormatter(50)] + : null, + obscureText: _obscureTextNotifier.value, + decoration: InputDecoration( + hintText: widget.hintText, + hintStyle: textTheme.body.copyWith(color: colorScheme.textMuted), + filled: true, + fillColor: colorScheme.fillFaint, + contentPadding: const EdgeInsets.fromLTRB( + 12, + 12, + 0, + 12, + ), + border: const UnderlineInputBorder( + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide( + color: _incorrectPassword + ? colorScheme.warning500 + : colorScheme.strokeFaint, + ), + borderRadius: BorderRadius.circular(8), + ), + suffixIcon: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: SuffixIconWidget( + key: ValueKey(executionState), + executionState: executionState, + shouldSurfaceExecutionStates: + widget.shouldSurfaceExecutionStates, + obscureTextNotifier: _obscureTextNotifier, + isPasswordInput: widget.isPasswordInput, + textController: _textController, + isCancellable: widget.cancellable, + shouldUnfocusOnCancelOrSubmit: + widget.shouldUnfocusOnCancelOrSubmit, + ), + ), + ), + prefixIconConstraints: const BoxConstraints( + maxHeight: 44, + maxWidth: 44, + minHeight: 44, + minWidth: 44, + ), + suffixIconConstraints: const BoxConstraints( + maxHeight: 24, + maxWidth: 48, + minHeight: 24, + minWidth: 48, + ), + prefixIcon: widget.prefixIcon != null + ? Icon( + widget.prefixIcon, + color: colorScheme.strokeMuted, + ) + : null, + ), + onEditingComplete: () { + _onSubmit(); + }, + ), + ), + ), + ); + if (widget.message != null) { + textInputChildren.add( + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Align( + alignment: widget.alignMessage ?? Alignment.centerLeft, + child: Text( + widget.message!, + style: textTheme.small.copyWith(color: colorScheme.textMuted), + ), + ), + ), + ); + } + textInputChildren = + addSeparators(textInputChildren, const SizedBox(height: 4)); + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: textInputChildren, + ); + } + + void _safeRefresh() { + if (mounted) { + setState(() {}); + } + } + + void _onSubmit() async { + _debouncer.run( + () => Future(() { + setState(() { + executionState = ExecutionState.inProgress; + }); + }), + ); + if (widget.shouldUnfocusOnCancelOrSubmit) { + FocusScope.of(context).unfocus(); + } + try { + await widget.onSubmit!.call(_textController.text); + } catch (e) { + executionState = ExecutionState.error; + _debouncer.cancelDebounce(); + _exception = e as Exception; + if (e.toString().contains("Incorrect password")) { + _logger.warning("Incorrect password"); + _surfaceWrongPasswordState(); + } + if (!widget.popNavAfterSubmission) { + rethrow; + } + } + widget.alwaysShowSuccessState && _debouncer.isActive() + ? executionState = ExecutionState.successful + : null; + _debouncer.cancelDebounce(); + if (executionState == ExecutionState.successful) { + setState(() {}); + } + + // when the time taken by widget.onSubmit is approximately equal to the debounce + // time, the callback is getting executed when/after the if condition + // below is executing/executed which results in execution state stuck at + // idle state. This Future is for delaying the execution of the if + // condition so that the calback in the debouncer finishes execution before. + await Future.delayed(const Duration(milliseconds: 5)); + if (executionState == ExecutionState.inProgress || + executionState == ExecutionState.error) { + if (executionState == ExecutionState.inProgress) { + if (mounted) { + if (widget.showOnlyLoadingState) { + setState(() { + executionState = ExecutionState.idle; + }); + _popNavigatorStack(context); + } else { + setState(() { + executionState = ExecutionState.successful; + Future.delayed( + Duration( + seconds: widget.shouldSurfaceExecutionStates + ? (widget.popNavAfterSubmission ? 1 : 2) + : 0, + ), () { + widget.popNavAfterSubmission + ? _popNavigatorStack(context) + : null; + if (mounted) { + setState(() { + executionState = ExecutionState.idle; + }); + } + }); + }); + } + } + } + if (executionState == ExecutionState.error) { + setState(() { + executionState = ExecutionState.idle; + widget.popNavAfterSubmission + ? Future.delayed( + const Duration(seconds: 0), + () => _popNavigatorStack(context, e: _exception), + ) + : null; + }); + } + } else { + if (widget.popNavAfterSubmission) { + Future.delayed( + Duration(seconds: widget.alwaysShowSuccessState ? 1 : 0), + () => _popNavigatorStack(context), + ); + } + } + } + + void _popNavigatorStack(BuildContext context, {Exception? e}) { + Navigator.of(context).canPop() ? Navigator.of(context).pop(e) : null; + } + + void _surfaceWrongPasswordState() { + setState(() { + _incorrectPassword = true; + HapticFeedback.vibrate(); + Future.delayed(const Duration(seconds: 1), () { + if (mounted) { + setState(() { + _incorrectPassword = false; + }); + } + }); + }); + } +} + +//todo: Add clear and custom icon for suffic icon +class SuffixIconWidget extends StatelessWidget { + final ExecutionState executionState; + final bool shouldSurfaceExecutionStates; + final TextEditingController textController; + final ValueNotifier? obscureTextNotifier; + final bool isPasswordInput; + final bool isCancellable; + final bool shouldUnfocusOnCancelOrSubmit; + + const SuffixIconWidget({ + required this.executionState, + required this.shouldSurfaceExecutionStates, + required this.textController, + this.obscureTextNotifier, + this.isPasswordInput = false, + this.isCancellable = false, + this.shouldUnfocusOnCancelOrSubmit = false, + super.key, + }); + + @override + Widget build(BuildContext context) { + final Widget trailingWidget; + final colorScheme = getEnteColorScheme(context); + if (executionState == ExecutionState.idle || + !shouldSurfaceExecutionStates) { + if (isCancellable) { + trailingWidget = GestureDetector( + onTap: () { + textController.clear(); + if (shouldUnfocusOnCancelOrSubmit) { + FocusScope.of(context).unfocus(); + } + }, + child: Icon( + Icons.cancel_rounded, + color: colorScheme.strokeMuted, + ), + ); + } else if (isPasswordInput) { + assert(obscureTextNotifier != null); + trailingWidget = GestureDetector( + onTap: () { + obscureTextNotifier!.value = !obscureTextNotifier!.value; + }, + child: Icon( + obscureTextNotifier!.value + ? Icons.visibility_off_outlined + : Icons.visibility, + color: obscureTextNotifier!.value ? colorScheme.strokeMuted : null, + ), + ); + } else { + trailingWidget = const SizedBox.shrink(); + } + } else if (executionState == ExecutionState.inProgress) { + trailingWidget = EnteLoadingWidget( + color: colorScheme.strokeMuted, + ); + } else if (executionState == ExecutionState.successful) { + trailingWidget = Icon( + Icons.check_outlined, + size: 22, + color: colorScheme.primary500, + ); + } else { + trailingWidget = const SizedBox.shrink(); + } + return trailingWidget; + } +} diff --git a/mobile/packages/ui/lib/components/title_bar_title_widget.dart b/mobile/packages/ui/lib/components/title_bar_title_widget.dart new file mode 100644 index 0000000000..3af955fea3 --- /dev/null +++ b/mobile/packages/ui/lib/components/title_bar_title_widget.dart @@ -0,0 +1,55 @@ +import '../theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class TitleBarTitleWidget extends StatelessWidget { + final String? title; + final bool isTitleH2; + final IconData? icon; + const TitleBarTitleWidget({ + super.key, + this.title, + this.isTitleH2 = false, + this.icon, + }); + + @override + Widget build(BuildContext context) { + final textTheme = getEnteTextTheme(context); + final colorTheme = getEnteColorScheme(context); + if (title != null) { + if (icon != null) { + return Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + title!, + style: textTheme.h3Bold, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + const SizedBox(width: 8), + Icon(icon, size: 20, color: colorTheme.strokeMuted), + ], + ); + } + if (isTitleH2) { + return Text( + title!, + style: textTheme.h2Bold, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ); + } else { + return Text( + title!, + style: textTheme.h3Bold, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ); + } + } + + return const SizedBox.shrink(); + } +} diff --git a/mobile/packages/ui/lib/components/title_bar_widget.dart b/mobile/packages/ui/lib/components/title_bar_widget.dart new file mode 100644 index 0000000000..9517bc1749 --- /dev/null +++ b/mobile/packages/ui/lib/components/title_bar_widget.dart @@ -0,0 +1,152 @@ +import '../theme/ente_theme.dart'; +import '../components/buttons/icon_button_widget.dart'; +import 'package:flutter/material.dart'; + +class TitleBarWidget extends StatelessWidget { + final IconButtonWidget? leading; + final String? title; + final String? caption; + final Widget? flexibleSpaceTitle; + final String? flexibleSpaceCaption; + final List? actionIcons; + final bool isTitleH2WithoutLeading; + final bool isFlexibleSpaceDisabled; + final bool isOnTopOfScreen; + final Color? backgroundColor; + const TitleBarWidget({ + super.key, + this.leading, + this.title, + this.caption, + this.flexibleSpaceTitle, + this.flexibleSpaceCaption, + this.actionIcons, + this.isTitleH2WithoutLeading = false, + this.isFlexibleSpaceDisabled = false, + this.isOnTopOfScreen = true, + this.backgroundColor, + }); + + @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), + ), + ], + ), + ), + 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, + ), + ], + ), + ), + ], + ), + ), + ), + ); + } + + _actionsWithPaddingInBetween() { + if (actionIcons == null) { + return [const SizedBox.shrink()]; + } + final actions = []; + bool addWhiteSpace = false; + final length = actionIcons!.length; + int index = 0; + if (length == 0) { + return [const SizedBox.shrink()]; + } + if (length == 1) { + return actionIcons; + } + while (index < length) { + if (!addWhiteSpace) { + actions.add(actionIcons![index]); + index++; + addWhiteSpace = true; + } else { + actions.add(const SizedBox(width: 4)); + addWhiteSpace = false; + } + } + return actions; + } +} diff --git a/mobile/packages/ui/lib/components/toggle_switch_widget.dart b/mobile/packages/ui/lib/components/toggle_switch_widget.dart new file mode 100644 index 0000000000..438bcf177f --- /dev/null +++ b/mobile/packages/ui/lib/components/toggle_switch_widget.dart @@ -0,0 +1,136 @@ +import 'package:ente_base/typedefs.dart'; +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/models/execution_states.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/debouncer.dart'; +import 'package:flutter/material.dart'; + +typedef OnChangedCallBack = void Function(bool); + +class ToggleSwitchWidget extends StatefulWidget { + final BoolCallBack value; + final FutureVoidCallback onChanged; + const ToggleSwitchWidget({ + required this.value, + required this.onChanged, + super.key, + }); + + @override + State createState() => _ToggleSwitchWidgetState(); +} + +class _ToggleSwitchWidgetState extends State { + bool? toggleValue; + ExecutionState executionState = ExecutionState.idle; + final _debouncer = Debouncer(const Duration(milliseconds: 300)); + + @override + void initState() { + toggleValue = widget.value.call(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final enteColorScheme = getEnteColorScheme(context); + final Widget stateIcon = _stateIcon(enteColorScheme); + + return Row( + children: [ + Padding( + padding: const EdgeInsets.only(right: 2), + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 175), + switchInCurve: Curves.easeInExpo, + switchOutCurve: Curves.easeOutExpo, + child: stateIcon, + ), + ), + SizedBox( + height: 31, + child: FittedBox( + fit: BoxFit.contain, + child: Switch.adaptive( + activeColor: enteColorScheme.primary400, + activeTrackColor: enteColorScheme.primary300, + inactiveTrackColor: enteColorScheme.fillMuted, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, + value: toggleValue ?? false, + onChanged: (negationOfToggleValue) async { + setState(() { + toggleValue = negationOfToggleValue; + //start showing inProgress statu icons if toggle takes more than debounce time + _debouncer.run( + () => Future( + () { + setState(() { + executionState = ExecutionState.inProgress; + }); + }, + ), + ); + }); + final Stopwatch stopwatch = Stopwatch()..start(); + await widget.onChanged.call().onError( + (error, stackTrace) => _debouncer.cancelDebounce(), + ); + //for toggle feedback on short unsuccessful onChanged + await _feedbackOnUnsuccessfulToggle(stopwatch); + //debouncer gets canceled if onChanged takes less than debounce time + _debouncer.cancelDebounce(); + + final newValue = widget.value.call(); + setState(() { + if (toggleValue == newValue) { + if (executionState == ExecutionState.inProgress) { + executionState = ExecutionState.successful; + Future.delayed(const Duration(seconds: 2), () { + setState(() { + executionState = ExecutionState.idle; + }); + }); + } + } else { + toggleValue = !toggleValue!; + executionState = ExecutionState.idle; + } + }); + }, + ), + ), + ), + ], + ); + } + + Widget _stateIcon(enteColorScheme) { + if (executionState == ExecutionState.idle) { + return const SizedBox(width: 24); + } else if (executionState == ExecutionState.inProgress) { + return EnteLoadingWidget( + color: enteColorScheme.strokeMuted, + ); + } else if (executionState == ExecutionState.successful) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 1), + child: Icon( + Icons.check_outlined, + size: 22, + color: enteColorScheme.primary500, + ), + ); + } else { + return const SizedBox(width: 24); + } + } + + Future _feedbackOnUnsuccessfulToggle(Stopwatch stopwatch) async { + final timeElapsed = stopwatch.elapsedMilliseconds; + if (timeElapsed < 200) { + await Future.delayed( + Duration(milliseconds: 200 - timeElapsed), + ); + } + } +} diff --git a/mobile/packages/ui/lib/lifecycle_event_handler.dart b/mobile/packages/ui/lib/lifecycle_event_handler.dart new file mode 100644 index 0000000000..da5dfb3d97 --- /dev/null +++ b/mobile/packages/ui/lib/lifecycle_event_handler.dart @@ -0,0 +1,31 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +class LifecycleEventHandler extends WidgetsBindingObserver { + final AsyncCallback? resumeCallBack; + final AsyncCallback? suspendingCallBack; + + LifecycleEventHandler({ + this.resumeCallBack, + this.suspendingCallBack, + }); + + @override + Future didChangeAppLifecycleState(AppLifecycleState state) async { + switch (state) { + case AppLifecycleState.resumed: + if (resumeCallBack != null) { + await resumeCallBack!(); + } + break; + case AppLifecycleState.inactive: + case AppLifecycleState.hidden: + case AppLifecycleState.paused: + case AppLifecycleState.detached: + if (suspendingCallBack != null) { + await suspendingCallBack!(); + } + break; + } + } +} diff --git a/mobile/packages/ui/lib/models/execution_states.dart b/mobile/packages/ui/lib/models/execution_states.dart new file mode 100644 index 0000000000..9522030c14 --- /dev/null +++ b/mobile/packages/ui/lib/models/execution_states.dart @@ -0,0 +1,6 @@ +enum ExecutionState { + idle, + inProgress, + error, + successful +} diff --git a/mobile/packages/ui/lib/pages/log_file_viewer.dart b/mobile/packages/ui/lib/pages/log_file_viewer.dart new file mode 100644 index 0000000000..d002556852 --- /dev/null +++ b/mobile/packages/ui/lib/pages/log_file_viewer.dart @@ -0,0 +1,65 @@ +import 'dart:io'; + +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/cupertino.dart'; + +class LogFileViewer extends StatefulWidget { + final File file; + const LogFileViewer(this.file, {super.key}); + + @override + State createState() => _LogFileViewerState(); +} + +class _LogFileViewerState extends State { + String? _logs; + @override + void initState() { + widget.file.readAsString().then((logs) { + setState(() { + _logs = logs; + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: const Text("Today's logs"), + ), + body: _getBody(), + ); + } + + Widget _getBody() { + if (_logs == null) { + return const EnteLoadingWidget(); + } + return Container( + padding: const EdgeInsets.only(left: 12, top: 8, right: 12), + child: SingleChildScrollView( + child: SelectableRegion( + focusNode: FocusNode(), + selectionControls: Platform.isAndroid + ? materialTextSelectionControls + : Platform.isIOS + ? cupertinoTextSelectionControls + : desktopTextSelectionControls, + child: Text( + _logs!, + style: const TextStyle( + fontFeatures: [ + FontFeature.tabularFigures(), + ], + height: 1.2, + ), + ), + ), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/pages/web_page.dart b/mobile/packages/ui/lib/pages/web_page.dart new file mode 100644 index 0000000000..10652d0e51 --- /dev/null +++ b/mobile/packages/ui/lib/pages/web_page.dart @@ -0,0 +1,43 @@ +import '../components/loading_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_inappwebview/flutter_inappwebview.dart'; + +class WebPage extends StatefulWidget { + final String title; + final String url; + + const WebPage(this.title, this.url, {super.key}); + + @override + State createState() => _WebPageState(); +} + +class _WebPageState extends State { + bool _hasLoadedPage = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + // force dark theme for appBar till website/family plans add supports for light theme + backgroundColor: const Color.fromRGBO(10, 20, 20, 1.0), + foregroundColor: Colors.white, + iconTheme: const IconThemeData(color: Colors.white), + title: Text(widget.title), + actions: [_hasLoadedPage ? Container() : const EnteLoadingWidget()], + ), + backgroundColor: Colors.black, + body: InAppWebView( + initialUrlRequest: URLRequest(url: WebUri(widget.url)), + initialSettings: InAppWebViewSettings( + transparentBackground: true, + ), + onLoadStop: (c, url) { + setState(() { + _hasLoadedPage = true; + }); + }, + ), + ); + } +} diff --git a/mobile/packages/ui/lib/theme/color_migration_demo.dart b/mobile/packages/ui/lib/theme/color_migration_demo.dart new file mode 100644 index 0000000000..0f667ea910 --- /dev/null +++ b/mobile/packages/ui/lib/theme/color_migration_demo.dart @@ -0,0 +1,206 @@ +// Demo showing the color system migration is complete +// This file demonstrates that the new color system works correctly + +import 'package:flutter/material.dart'; +import '../theme/colors.dart'; +import '../theme/ente_theme.dart'; +import '../theme/ente_theme_data.dart'; + +/// Demo widget showing the new color system in action +class ColorMigrationDemo extends StatelessWidget { + const ColorMigrationDemo({super.key}); + + @override + Widget build(BuildContext context) { + // NEW: Preferred way to access colors + final colorScheme = Theme.of(context).extension() ?? + getEnteColorScheme(context); + + return Scaffold( + backgroundColor: colorScheme.backgroundBase, + appBar: AppBar( + backgroundColor: colorScheme.backgroundElevated, + title: Text( + 'Color Migration Demo', + style: TextStyle(color: colorScheme.textBase), + ), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildSection( + colorScheme, + 'Background Colors', + color: colorScheme.backgroundElevated, + border: colorScheme.strokeFaint, + ), + const SizedBox(height: 16), + _buildSection( + colorScheme, + 'Primary Colors', + color: colorScheme.primary500, + textColor: colorScheme.backgroundBase, + ), + const SizedBox(height: 16), + _buildGradientSection(colorScheme), + const SizedBox(height: 16), + _buildSection( + colorScheme, + 'Warning Colors', + color: colorScheme.warning500.withOpacity(0.1), + border: colorScheme.warning500, + textColor: colorScheme.warning700, + ), + const SizedBox(height: 16), + _buildSection( + colorScheme, + 'Fill Colors', + color: colorScheme.fillFaint, + textColor: colorScheme.textMuted, + ), + const SizedBox(height: 16), + _buildMigrationInfo(colorScheme), + ], + ), + ), + ); + } + + Widget _buildSection( + EnteColorScheme colorScheme, + String title, { + required Color color, + Color? border, + Color? textColor, + }) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: color, + border: border != null ? Border.all(color: border) : null, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + '$title ✅', + style: TextStyle( + color: textColor ?? colorScheme.textBase, + fontWeight: FontWeight.w500, + ), + ), + ); + } + + Widget _buildGradientSection(EnteColorScheme colorScheme) { + return Container( + height: 60, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: colorScheme.gradientButtonBgColors, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + 'Gradient Colors ✅', + style: TextStyle( + color: colorScheme.backgroundBase, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ); + } + + Widget _buildMigrationInfo(EnteColorScheme colorScheme) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.primary500.withOpacity(0.1), + border: Border.all(color: colorScheme.primary500), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Migration Complete! 🎉', + style: TextStyle( + color: colorScheme.primary700, + fontWeight: FontWeight.bold, + fontSize: 18, + ), + ), + const SizedBox(height: 8), + Text( + '• All components migrated to new color system\n' + '• Theme-aware colors with fallback support\n' + '• Custom branding support ready\n' + '• Performance optimized with const colors\n' + '• Type-safe with compile-time validation', + style: TextStyle( + color: colorScheme.textBase, + height: 1.5, + ), + ), + ], + ), + ); + } +} + +/// Example of creating custom app themes +class CustomAppThemeExample { + // Purple brand theme + static final purpleScheme = ColorSchemeBuilder.fromPrimaryColor( + const Color(0xFF6C5CE7), + ); + + // Blue brand theme + static final blueScheme = ColorSchemeBuilder.fromPrimaryColor( + const Color(0xFF2196F3), + ); + + // Green brand theme + static final greenScheme = ColorSchemeBuilder.fromPrimaryColor( + const Color(0xFF4CAF50), + ); + + // Create theme data for each brand + static ThemeData purpleLightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: purpleScheme.light, + ); + + static ThemeData purpleDarkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: purpleScheme.dark, + ); + + static ThemeData blueLightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: blueScheme.light, + ); + + static ThemeData blueDarkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: blueScheme.dark, + ); +} + +/// Example app showing how to use the new color system +class ColorSystemExampleApp extends StatelessWidget { + const ColorSystemExampleApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Color System Demo', + theme: CustomAppThemeExample.blueLightTheme, + darkTheme: CustomAppThemeExample.blueDarkTheme, + home: const ColorMigrationDemo(), + ); + } +} diff --git a/mobile/packages/ui/lib/theme/color_system_test.dart b/mobile/packages/ui/lib/theme/color_system_test.dart new file mode 100644 index 0000000000..00f8018a51 --- /dev/null +++ b/mobile/packages/ui/lib/theme/color_system_test.dart @@ -0,0 +1,161 @@ +// Test to validate the color system migration +// This file is for verification purposes only + +import 'package:flutter/material.dart'; +import '../theme/colors.dart'; +import '../theme/ente_theme.dart'; +import '../theme/ente_theme_data.dart'; + +/// Test widget to verify the new color system works correctly +class ColorSystemTestWidget extends StatelessWidget { + const ColorSystemTestWidget({super.key}); + + @override + Widget build(BuildContext context) { + // Test that we can access the color scheme from theme + final colorScheme = Theme.of(context).extension(); + + // Test that fallback to old system works + final fallbackColorScheme = colorScheme ?? getEnteColorScheme(context); + + return Scaffold( + backgroundColor: fallbackColorScheme.backgroundBase, + appBar: AppBar( + backgroundColor: fallbackColorScheme.backgroundElevated, + title: Text( + 'Color System Test', + style: TextStyle(color: fallbackColorScheme.textBase), + ), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Test basic colors + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: fallbackColorScheme.backgroundElevated, + border: Border.all(color: fallbackColorScheme.strokeFaint), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'Background Colors Working', + style: TextStyle(color: fallbackColorScheme.textBase), + ), + ), + const SizedBox(height: 16), + + // Test primary colors + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: fallbackColorScheme.primary500, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'Primary Colors Working', + style: TextStyle(color: fallbackColorScheme.backgroundBase), + ), + ), + const SizedBox(height: 16), + + // Test gradient colors + Container( + height: 50, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: fallbackColorScheme.gradientButtonBgColors, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + 'Gradient Colors Working', + style: TextStyle( + color: fallbackColorScheme.backgroundBase, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + const SizedBox(height: 16), + + // Test warning colors + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: fallbackColorScheme.warning500.withOpacity(0.1), + border: Border.all(color: fallbackColorScheme.warning500), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'Warning Colors Working', + style: TextStyle(color: fallbackColorScheme.warning700), + ), + ), + const SizedBox(height: 16), + + // Test fill colors + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: fallbackColorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + 'Fill Colors Working', + style: TextStyle(color: fallbackColorScheme.textMuted), + ), + ), + ], + ), + ), + ); + } +} + +/// Example showing how apps can create custom themes +class CustomThemeExample { + // Create a custom purple theme + static final purpleSchemes = ColorSchemeBuilder.fromPrimaryColor( + const Color(0xFF6C5CE7), // Purple brand color + ); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: purpleSchemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: purpleSchemes.dark, + ); +} + +/// Example showing migration from old to new system +class MigrationExample extends StatelessWidget { + const MigrationExample({super.key}); + + @override + Widget build(BuildContext context) { + // OLD WAY (still works for backward compatibility) + // final colorScheme = getEnteColorScheme(context); + + // NEW WAY (preferred) + // final colorScheme = Theme.of(context).extension()!; + + // SAFE WAY (with fallback) + final safeColorScheme = Theme.of(context).extension() ?? + getEnteColorScheme(context); + + return Container( + color: safeColorScheme.backgroundBase, + child: Text( + 'Migration Example', + style: TextStyle(color: safeColorScheme.textBase), + ), + ); + } +} diff --git a/mobile/packages/ui/lib/theme/colors.dart b/mobile/packages/ui/lib/theme/colors.dart new file mode 100644 index 0000000000..aa0f6274c5 --- /dev/null +++ b/mobile/packages/ui/lib/theme/colors.dart @@ -0,0 +1,832 @@ +import 'package:flutter/material.dart'; + +/// A comprehensive color scheme for consistent theming across apps. +/// +/// This color scheme provides all the colors needed for a modern Flutter app, +/// including background, text, fill, stroke, and accent colors for both light +/// and dark themes. +/// +/// Apps can easily customize the primary colors using the factory constructors: +/// +/// ```dart +/// // Create a light theme with custom primary colors +/// final customLightScheme = EnteColorScheme.light( +/// primary700: Color(0xFF1976D2), +/// primary500: Color(0xFF2196F3), +/// primary400: Color(0xFF42A5F5), +/// primary300: Color(0xFF64B5F6), +/// ); +/// +/// // Create a dark theme with custom primary colors +/// final customDarkScheme = EnteColorScheme.dark( +/// primary700: Color(0xFF1976D2), +/// primary500: Color(0xFF2196F3), +/// primary400: Color(0xFF42A5F5), +/// primary300: Color(0xFF64B5F6), +/// ); +/// ``` +class EnteColorScheme extends ThemeExtension { + // Background Colors + final Color backgroundBase; + final Color backgroundElevated; + final Color backgroundElevated2; + + // Backdrop Colors + final Color backdropBase; + final Color backdropBaseMute; + final Color backdropFaint; + + // Text Colors + final Color textBase; + final Color textMuted; + final Color textFaint; + + // Fill Colors + final Color fillBase; + final Color fillBasePressed; + final Color fillMuted; + final Color fillFaint; + final Color fillFaintPressed; + + // Stroke Colors + final Color strokeBase; + final Color strokeMuted; + final Color strokeFaint; + final Color strokeFainter; + final Color blurStrokeBase; + final Color blurStrokeFaint; + final Color blurStrokePressed; + + // Fixed Colors + final Color primary700; + final Color primary500; + final Color primary400; + final Color primary300; + + final Color iconButtonColor; + + final Color warning700; + final Color warning500; + final Color warning400; + final Color warning800; + + final Color caution500; + + // Gradient Button + final Color gradientButtonBgColor; + final List gradientButtonBgColors; + + // Additional colors from ente_theme_data + final Color fabForegroundColor; + final Color fabBackgroundColor; + final Color boxSelectColor; + final Color boxUnSelectColor; + final Color alternativeColor; + final Color dynamicFABBackgroundColor; + final Color dynamicFABTextColor; + final Color recoveryKeyBoxColor; + final Color frostyBlurBackdropFilterColor; + final Color iconColor; + final Color bgColorForQuestions; + final Color greenText; + final Color cupertinoPickerTopColor; + final Color stepProgressUnselectedColor; + final Color gNavBackgroundColor; + final Color gNavBarActiveColor; + final Color gNavIconColor; + final Color gNavActiveIconColor; + final Color galleryThumbBackgroundColor; + final Color galleryThumbDrawColor; + final Color backupEnabledBgColor; + final Color dotsIndicatorActiveColor; + final Color dotsIndicatorInactiveColor; + final Color toastTextColor; + final Color toastBackgroundColor; + final Color subTextColor; + final Color themeSwitchInactiveIconColor; + final Color searchResultsColor; + final Color mutedTextColor; + final Color searchResultsBackgroundColor; + final Color codeCardBackgroundColor; + final Color primaryColor; + final Color surface; + + bool get isLightTheme => backgroundBase == backgroundBaseLight; + + const EnteColorScheme( + this.backgroundBase, + this.backgroundElevated, + this.backgroundElevated2, + this.backdropBase, + this.backdropBaseMute, + this.backdropFaint, + this.textBase, + this.textMuted, + this.textFaint, + this.fillBase, + this.fillBasePressed, + this.fillMuted, + this.fillFaint, + this.fillFaintPressed, + this.strokeBase, + this.strokeMuted, + this.strokeFaint, + this.strokeFainter, + this.blurStrokeBase, + this.blurStrokeFaint, + this.blurStrokePressed, + this.iconButtonColor, + this.gradientButtonBgColor, + this.gradientButtonBgColors, + this.primary700, + this.primary500, + this.primary400, + this.primary300, { + this.warning700 = _warning700, + this.warning800 = _warning800, + this.warning500 = _warning500, + this.warning400 = _warning700, + this.caution500 = _caution500, + this.fabForegroundColor = _defaultFabForegroundColor, + this.fabBackgroundColor = _defaultFabBackgroundColor, + this.boxSelectColor = _defaultBoxSelectColor, + this.boxUnSelectColor = _defaultBoxUnSelectColor, + this.alternativeColor = _defaultAlternativeColor, + this.dynamicFABBackgroundColor = _defaultDynamicFABBackgroundColor, + this.dynamicFABTextColor = _defaultDynamicFABTextColor, + this.recoveryKeyBoxColor = _defaultRecoveryKeyBoxColor, + this.frostyBlurBackdropFilterColor = _defaultFrostyBlurBackdropFilterColor, + this.iconColor = _defaultIconColor, + this.bgColorForQuestions = _defaultBgColorForQuestions, + this.greenText = _defaultGreenText, + this.cupertinoPickerTopColor = _defaultCupertinoPickerTopColor, + this.stepProgressUnselectedColor = _defaultStepProgressUnselectedColor, + this.gNavBackgroundColor = _defaultGNavBackgroundColor, + this.gNavBarActiveColor = _defaultGNavBarActiveColor, + this.gNavIconColor = _defaultGNavIconColor, + this.gNavActiveIconColor = _defaultGNavActiveIconColor, + this.galleryThumbBackgroundColor = _defaultGalleryThumbBackgroundColor, + this.galleryThumbDrawColor = _defaultGalleryThumbDrawColor, + this.backupEnabledBgColor = _defaultBackupEnabledBgColor, + this.dotsIndicatorActiveColor = _defaultDotsIndicatorActiveColor, + this.dotsIndicatorInactiveColor = _defaultDotsIndicatorInactiveColor, + this.toastTextColor = _defaultToastTextColor, + this.toastBackgroundColor = _defaultToastBackgroundColor, + this.subTextColor = _defaultSubTextColor, + this.themeSwitchInactiveIconColor = _defaultThemeSwitchInactiveIconColor, + this.searchResultsColor = _defaultSearchResultsColor, + this.mutedTextColor = _defaultMutedTextColor, + this.searchResultsBackgroundColor = _defaultSearchResultsBackgroundColor, + this.codeCardBackgroundColor = _defaultCodeCardBackgroundColor, + this.primaryColor = _defaultPrimaryColor, + this.surface = _defaultPrimaryColor, + }); + + /// Factory constructor for light theme with customizable primary colors + factory EnteColorScheme.light({ + Color? primary700, + Color? primary500, + Color? primary400, + Color? primary300, + Color? iconButtonColor, + Color? gradientButtonBgColor, + List? gradientButtonBgColors, + Color? warning700, + Color? warning500, + Color? warning400, + Color? warning800, + Color? caution500, + }) { + return EnteColorScheme( + backgroundBaseLight, + backgroundElevatedLight, + backgroundElevated2Light, + backdropBaseLight, + backdropMutedLight, + backdropFaintLight, + textBaseLight, + textMutedLight, + textFaintLight, + fillBaseLight, + fillBasePressedLight, + fillMutedLight, + fillFaintLight, + fillFaintPressedLight, + strokeBaseLight, + strokeMutedLight, + strokeFaintLight, + strokeFainterLight, + blurStrokeBaseLight, + blurStrokeFaintLight, + blurStrokePressedLight, + iconButtonColor ?? _defaultIconButtonColor, + gradientButtonBgColor ?? _defaultGradientButtonBgColor, + gradientButtonBgColors ?? _defaultGradientButtonBgColors, + primary700 ?? _defaultPrimary700, + primary500 ?? _defaultPrimary500, + primary400 ?? _defaultPrimary400, + primary300 ?? _defaultPrimary300, + alternativeColor: primary400 ?? _defaultAlternativeColor, + warning700: warning700 ?? _warning700, + warning800: warning800 ?? _warning800, + warning500: warning500 ?? _warning500, + warning400: warning400 ?? _warning700, + caution500: caution500 ?? _caution500, + ); + } + + /// Factory constructor for dark theme with customizable primary colors + factory EnteColorScheme.dark({ + Color? primary700, + Color? primary500, + Color? primary400, + Color? primary300, + Color? iconButtonColor, + Color? gradientButtonBgColor, + List? gradientButtonBgColors, + Color? warning700, + Color? warning500, + Color? warning400, + Color? warning800, + Color? caution500, + }) { + return EnteColorScheme( + backgroundBaseDark, + backgroundElevatedDark, + backgroundElevated2Dark, + backdropBaseDark, + backdropMutedDark, + backdropFaintDark, + textBaseDark, + textMutedDark, + textFaintDark, + fillBaseDark, + fillBasePressedDark, + fillMutedDark, + fillFaintDark, + fillFaintPressedDark, + strokeBaseDark, + strokeMutedDark, + strokeFaintDark, + strokeFainterDark, + blurStrokeBaseDark, + blurStrokeFaintDark, + blurStrokePressedDark, + iconButtonColor ?? _defaultIconButtonColor, + gradientButtonBgColor ?? _defaultGradientButtonBgColor, + gradientButtonBgColors ?? _defaultGradientButtonBgColors, + primary700 ?? _defaultPrimary700, + primary500 ?? _defaultPrimary500, + primary400 ?? _defaultPrimary400, + primary300 ?? _defaultPrimary300, + alternativeColor: primary400 ?? _defaultAlternativeColor, + warning700: warning700 ?? _warning700, + warning800: warning800 ?? _warning800, + warning500: warning500 ?? _warning500, + warning400: warning400 ?? _warning700, + caution500: caution500 ?? _caution500, + ); + } + + get inverseEnteTheme => null; + + @override + EnteColorScheme copyWith({ + Color? backgroundBase, + Color? backgroundElevated, + Color? backgroundElevated2, + Color? backdropBase, + Color? backdropBaseMute, + Color? backdropFaint, + Color? textBase, + Color? textMuted, + Color? textFaint, + Color? fillBase, + Color? fillBasePressed, + Color? fillMuted, + Color? fillFaint, + Color? fillFaintPressed, + Color? strokeBase, + Color? strokeMuted, + Color? strokeFaint, + Color? strokeFainter, + Color? blurStrokeBase, + Color? blurStrokeFaint, + Color? blurStrokePressed, + Color? primary700, + Color? primary500, + Color? primary400, + Color? primary300, + Color? iconButtonColor, + Color? warning700, + Color? warning500, + Color? warning400, + Color? warning800, + Color? caution500, + Color? gradientButtonBgColor, + List? gradientButtonBgColors, + Color? fabForegroundColor, + Color? fabBackgroundColor, + Color? boxSelectColor, + Color? boxUnSelectColor, + Color? alternativeColor, + Color? dynamicFABBackgroundColor, + Color? dynamicFABTextColor, + Color? recoveryKeyBoxColor, + Color? frostyBlurBackdropFilterColor, + Color? iconColor, + Color? bgColorForQuestions, + Color? greenText, + Color? cupertinoPickerTopColor, + Color? stepProgressUnselectedColor, + Color? gNavBackgroundColor, + Color? gNavBarActiveColor, + Color? gNavIconColor, + Color? gNavActiveIconColor, + Color? galleryThumbBackgroundColor, + Color? galleryThumbDrawColor, + Color? backupEnabledBgColor, + Color? dotsIndicatorActiveColor, + Color? dotsIndicatorInactiveColor, + Color? toastTextColor, + Color? toastBackgroundColor, + Color? subTextColor, + Color? themeSwitchInactiveIconColor, + Color? searchResultsColor, + Color? mutedTextColor, + Color? searchResultsBackgroundColor, + Color? codeCardBackgroundColor, + Color? primaryColor, + }) { + return EnteColorScheme( + backgroundBase ?? this.backgroundBase, + backgroundElevated ?? this.backgroundElevated, + backgroundElevated2 ?? this.backgroundElevated2, + backdropBase ?? this.backdropBase, + backdropBaseMute ?? this.backdropBaseMute, + backdropFaint ?? this.backdropFaint, + textBase ?? this.textBase, + textMuted ?? this.textMuted, + textFaint ?? this.textFaint, + fillBase ?? this.fillBase, + fillBasePressed ?? this.fillBasePressed, + fillMuted ?? this.fillMuted, + fillFaint ?? this.fillFaint, + fillFaintPressed ?? this.fillFaintPressed, + strokeBase ?? this.strokeBase, + strokeMuted ?? this.strokeMuted, + strokeFaint ?? this.strokeFaint, + strokeFainter ?? this.strokeFainter, + blurStrokeBase ?? this.blurStrokeBase, + blurStrokeFaint ?? this.blurStrokeFaint, + blurStrokePressed ?? this.blurStrokePressed, + iconButtonColor ?? this.iconButtonColor, + gradientButtonBgColor ?? this.gradientButtonBgColor, + gradientButtonBgColors ?? this.gradientButtonBgColors, + primary700 ?? this.primary700, + primary500 ?? this.primary500, + primary400 ?? this.primary400, + primary300 ?? this.primary300, + warning700: warning700 ?? this.warning700, + warning800: warning800 ?? this.warning800, + warning500: warning500 ?? this.warning500, + warning400: warning400 ?? this.warning400, + caution500: caution500 ?? this.caution500, + fabForegroundColor: fabForegroundColor ?? this.fabForegroundColor, + fabBackgroundColor: fabBackgroundColor ?? this.fabBackgroundColor, + boxSelectColor: boxSelectColor ?? this.boxSelectColor, + boxUnSelectColor: boxUnSelectColor ?? this.boxUnSelectColor, + alternativeColor: alternativeColor ?? this.alternativeColor, + dynamicFABBackgroundColor: + dynamicFABBackgroundColor ?? this.dynamicFABBackgroundColor, + dynamicFABTextColor: dynamicFABTextColor ?? this.dynamicFABTextColor, + recoveryKeyBoxColor: recoveryKeyBoxColor ?? this.recoveryKeyBoxColor, + frostyBlurBackdropFilterColor: + frostyBlurBackdropFilterColor ?? this.frostyBlurBackdropFilterColor, + iconColor: iconColor ?? this.iconColor, + bgColorForQuestions: bgColorForQuestions ?? this.bgColorForQuestions, + greenText: greenText ?? this.greenText, + cupertinoPickerTopColor: + cupertinoPickerTopColor ?? this.cupertinoPickerTopColor, + stepProgressUnselectedColor: + stepProgressUnselectedColor ?? this.stepProgressUnselectedColor, + gNavBackgroundColor: gNavBackgroundColor ?? this.gNavBackgroundColor, + gNavBarActiveColor: gNavBarActiveColor ?? this.gNavBarActiveColor, + gNavIconColor: gNavIconColor ?? this.gNavIconColor, + gNavActiveIconColor: gNavActiveIconColor ?? this.gNavActiveIconColor, + galleryThumbBackgroundColor: + galleryThumbBackgroundColor ?? this.galleryThumbBackgroundColor, + galleryThumbDrawColor: + galleryThumbDrawColor ?? this.galleryThumbDrawColor, + backupEnabledBgColor: backupEnabledBgColor ?? this.backupEnabledBgColor, + dotsIndicatorActiveColor: + dotsIndicatorActiveColor ?? this.dotsIndicatorActiveColor, + dotsIndicatorInactiveColor: + dotsIndicatorInactiveColor ?? this.dotsIndicatorInactiveColor, + toastTextColor: toastTextColor ?? this.toastTextColor, + toastBackgroundColor: toastBackgroundColor ?? this.toastBackgroundColor, + subTextColor: subTextColor ?? this.subTextColor, + themeSwitchInactiveIconColor: + themeSwitchInactiveIconColor ?? this.themeSwitchInactiveIconColor, + searchResultsColor: searchResultsColor ?? this.searchResultsColor, + mutedTextColor: mutedTextColor ?? this.mutedTextColor, + searchResultsBackgroundColor: + searchResultsBackgroundColor ?? this.searchResultsBackgroundColor, + codeCardBackgroundColor: + codeCardBackgroundColor ?? this.codeCardBackgroundColor, + primaryColor: primaryColor ?? this.primaryColor, + ); + } + + @override + EnteColorScheme lerp(ThemeExtension? other, double t) { + if (other is! EnteColorScheme) { + return this; + } + + return EnteColorScheme( + Color.lerp(backgroundBase, other.backgroundBase, t)!, + Color.lerp(backgroundElevated, other.backgroundElevated, t)!, + Color.lerp(backgroundElevated2, other.backgroundElevated2, t)!, + Color.lerp(backdropBase, other.backdropBase, t)!, + Color.lerp(backdropBaseMute, other.backdropBaseMute, t)!, + Color.lerp(backdropFaint, other.backdropFaint, t)!, + Color.lerp(textBase, other.textBase, t)!, + Color.lerp(textMuted, other.textMuted, t)!, + Color.lerp(textFaint, other.textFaint, t)!, + Color.lerp(fillBase, other.fillBase, t)!, + Color.lerp(fillBasePressed, other.fillBasePressed, t)!, + Color.lerp(fillMuted, other.fillMuted, t)!, + Color.lerp(fillFaint, other.fillFaint, t)!, + Color.lerp(fillFaintPressed, other.fillFaintPressed, t)!, + Color.lerp(strokeBase, other.strokeBase, t)!, + Color.lerp(strokeMuted, other.strokeMuted, t)!, + Color.lerp(strokeFaint, other.strokeFaint, t)!, + Color.lerp(strokeFainter, other.strokeFainter, t)!, + Color.lerp(blurStrokeBase, other.blurStrokeBase, t)!, + Color.lerp(blurStrokeFaint, other.blurStrokeFaint, t)!, + Color.lerp(blurStrokePressed, other.blurStrokePressed, t)!, + Color.lerp(iconButtonColor, other.iconButtonColor, t)!, + Color.lerp(gradientButtonBgColor, other.gradientButtonBgColor, t)!, + _lerpColorList(gradientButtonBgColors, other.gradientButtonBgColors, t), + Color.lerp(primary700, other.primary700, t)!, + Color.lerp(primary500, other.primary500, t)!, + Color.lerp(primary400, other.primary400, t)!, + Color.lerp(primary300, other.primary300, t)!, + warning700: Color.lerp(warning700, other.warning700, t)!, + warning800: Color.lerp(warning800, other.warning800, t)!, + warning500: Color.lerp(warning500, other.warning500, t)!, + warning400: Color.lerp(warning400, other.warning400, t)!, + caution500: Color.lerp(caution500, other.caution500, t)!, + ); + } + + /// Helper method to lerp between two color lists + List _lerpColorList(List a, List b, double t) { + if (a.length != b.length) { + return t < 0.5 ? a : b; + } + return List.generate( + a.length, + (index) => Color.lerp(a[index], b[index], t)!, + ); + } +} + +const EnteColorScheme lightScheme = EnteColorScheme( + backgroundBaseLight, + backgroundElevatedLight, + backgroundElevated2Light, + backdropBaseLight, + backdropMutedLight, + backdropFaintLight, + textBaseLight, + textMutedLight, + textFaintLight, + fillBaseLight, + fillBasePressedLight, + fillMutedLight, + fillFaintLight, + fillFaintPressedLight, + strokeBaseLight, + strokeMutedLight, + strokeFaintLight, + strokeFainterLight, + blurStrokeBaseLight, + blurStrokeFaintLight, + blurStrokePressedLight, + _defaultIconButtonColor, + _defaultGradientButtonBgColor, + _defaultGradientButtonBgColors, + _defaultPrimary700, + _defaultPrimary500, + _defaultPrimary400, + _defaultPrimary300, +); + +const EnteColorScheme darkScheme = EnteColorScheme( + backgroundBaseDark, + backgroundElevatedDark, + backgroundElevated2Dark, + backdropBaseDark, + backdropMutedDark, + backdropFaintDark, + textBaseDark, + textMutedDark, + textFaintDark, + fillBaseDark, + fillBasePressedDark, + fillMutedDark, + fillFaintDark, + fillFaintPressedDark, + strokeBaseDark, + strokeMutedDark, + strokeFaintDark, + strokeFainterDark, + blurStrokeBaseDark, + blurStrokeFaintDark, + blurStrokePressedDark, + _defaultIconButtonColor, + _defaultGradientButtonBgColor, + _defaultGradientButtonBgColors, + _defaultPrimary700, + _defaultPrimary500, + _defaultPrimary400, + _defaultPrimary300, +); + +// Background Colors +const Color backgroundBaseLight = Color.fromRGBO(255, 255, 255, 1); +const Color backgroundElevatedLight = Color.fromRGBO(255, 255, 255, 1); +const Color backgroundElevated2Light = Color.fromRGBO(251, 251, 251, 1); + +const Color backgroundBaseDark = Color.fromRGBO(0, 0, 0, 1); +const Color backgroundElevatedDark = Color.fromRGBO(27, 27, 27, 1); +const Color backgroundElevated2Dark = Color.fromRGBO(37, 37, 37, 1); + +// Backdrop Colors +const Color backdropBaseLight = Color.fromRGBO(255, 255, 255, 0.92); +const Color backdropMutedLight = Color.fromRGBO(255, 255, 255, 0.75); +const Color backdropFaintLight = Color.fromRGBO(255, 255, 255, 0.30); + +const Color backdropBaseDark = Color.fromRGBO(0, 0, 0, 0.90); +const Color backdropMutedDark = Color.fromRGBO(0, 0, 0, 0.65); +const Color backdropFaintDark = Color.fromRGBO(0, 0, 0, 0.20); + +// Text Colors +const Color textBaseLight = Color.fromRGBO(0, 0, 0, 1); +const Color textMutedLight = Color.fromRGBO(0, 0, 0, 0.6); +const Color textFaintLight = Color.fromRGBO(0, 0, 0, 0.5); + +const Color textBaseDark = Color.fromRGBO(255, 255, 255, 1); +const Color textMutedDark = Color.fromRGBO(255, 255, 255, 0.7); +const Color textFaintDark = Color.fromRGBO(255, 255, 255, 0.5); + +// Fill Colors +const Color fillBaseLight = Color.fromRGBO(0, 0, 0, 1); +const Color fillBasePressedLight = Color.fromRGBO(0, 0, 0, 0.87); +const Color fillMutedLight = Color.fromRGBO(0, 0, 0, 0.12); +const Color fillFaintLight = Color.fromRGBO(0, 0, 0, 0.04); +const Color fillFaintPressedLight = Color.fromRGBO(0, 0, 0, 0.08); + +const Color fillBaseDark = Color.fromRGBO(255, 255, 255, 1); +const Color fillBasePressedDark = Color.fromRGBO(255, 255, 255, 0.9); +const Color fillMutedDark = Color.fromRGBO(255, 255, 255, 0.16); +const Color fillFaintDark = Color.fromRGBO(255, 255, 255, 0.12); +const Color fillFaintPressedDark = Color.fromRGBO(255, 255, 255, 0.06); + +// Stroke Colors +const Color strokeBaseLight = Color.fromRGBO(0, 0, 0, 1); +const Color strokeMutedLight = Color.fromRGBO(0, 0, 0, 0.24); +const Color strokeFaintLight = Color.fromRGBO(0, 0, 0, 0.04); +const Color strokeFainterLight = Color.fromRGBO(0, 0, 0, 0.06); +const Color blurStrokeBaseLight = Color.fromRGBO(0, 0, 0, 0.65); +const Color blurStrokeFaintLight = Color.fromRGBO(0, 0, 0, 0.08); +const Color blurStrokePressedLight = Color.fromRGBO(0, 0, 0, 0.50); + +const Color strokeBaseDark = Color.fromRGBO(255, 255, 255, 1); +const Color strokeMutedDark = Color.fromRGBO(255, 255, 255, 0.24); +const Color strokeFaintDark = Color.fromRGBO(255, 255, 255, 0.16); +const Color strokeFainterDark = Color.fromRGBO(255, 255, 255, 0.08); +const Color blurStrokeBaseDark = Color.fromRGBO(255, 255, 255, 0.90); +const Color blurStrokeFaintDark = Color.fromRGBO(255, 255, 255, 0.06); +const Color blurStrokePressedDark = Color.fromRGBO(255, 255, 255, 0.50); + +// Default Primary Colors +const Color _defaultPrimary700 = Color.fromRGBO(0, 122, 255, 1); +const Color _defaultPrimary500 = Color.fromRGBO(52, 152, 255, 1); +const Color _defaultPrimary400 = Color.fromRGBO(102, 178, 255, 1); +const Color _defaultPrimary300 = Color.fromRGBO(153, 204, 255, 1); + +// Default Gradient Colors +const Color _defaultGradientButtonBgColor = Color.fromRGBO(0, 122, 255, 1); +const List _defaultGradientButtonBgColors = [ + Color.fromRGBO(0, 122, 255, 1), + Color.fromRGBO(52, 152, 255, 1), +]; + +// Default Icon Button Color +const Color _defaultIconButtonColor = Color.fromRGBO(0, 122, 255, 1); + +// Warning Colors +const Color _warning700 = Color.fromRGBO(245, 52, 52, 1); +const Color _warning500 = Color.fromRGBO(255, 101, 101, 1); +const Color _warning800 = Color(0xFFF53434); +const Color warning500 = Color.fromRGBO(255, 101, 101, 1); +// ignore: unused_element +const Color _warning400 = Color.fromRGBO(255, 111, 111, 1); + +// Caution Colors +const Color _caution500 = Color.fromRGBO(255, 194, 71, 1); + +// Additional default colors from ente_theme_data +const Color _defaultPrimaryColor = Color(0xFF9610D6); + +// FAB Colors - based on brightness-dependent logic from ente_theme_data +const Color _defaultFabForegroundColor = Color.fromRGBO(255, 255, 255, 1); +const Color _defaultFabBackgroundColor = Color.fromRGBO(40, 40, 40, 1); + +// Box selection colors +const Color _defaultBoxSelectColor = Color.fromRGBO(67, 186, 108, 1); +const Color _defaultBoxUnSelectColor = Color.fromRGBO(240, 240, 240, 1); + +// Alternative color +const Color _defaultAlternativeColor = Color.fromARGB(255, 152, 77, 244); + +// Dynamic FAB colors +const Color _defaultDynamicFABBackgroundColor = Color.fromRGBO(0, 0, 0, 1); +const Color _defaultDynamicFABTextColor = Color.fromRGBO(255, 255, 255, 1); + +// Recovery key box color +const Color _defaultRecoveryKeyBoxColor = Color.fromARGB(51, 150, 0, 220); + +// Frosty blur backdrop filter color +const Color _defaultFrostyBlurBackdropFilterColor = + Color.fromRGBO(238, 238, 238, 0.5); + +// Default Icon Color +const Color _defaultIconColor = Color.fromRGBO(0, 0, 0, 0.75); + +// Default Background Color For Questions +const Color _defaultBgColorForQuestions = Color.fromRGBO(255, 255, 255, 1); + +// Default Green Text Color +const Color _defaultGreenText = Color.fromARGB(255, 40, 190, 113); + +// Default Cupertino Picker Top Color +const Color _defaultCupertinoPickerTopColor = + Color.fromARGB(255, 238, 238, 238); + +// Default Step Progress Unselected Color +const Color _defaultStepProgressUnselectedColor = + Color.fromRGBO(196, 196, 196, 0.6); + +// Default Navigation Colors +const Color _defaultGNavBackgroundColor = Color.fromRGBO(196, 196, 196, 0.6); +const Color _defaultGNavBarActiveColor = Color.fromRGBO(255, 255, 255, 0.6); +const Color _defaultGNavIconColor = Color.fromRGBO(0, 0, 0, 0.8); +const Color _defaultGNavActiveIconColor = Color.fromRGBO(0, 0, 0, 0.8); + +// Default Gallery Thumb Colors +const Color _defaultGalleryThumbBackgroundColor = + Color.fromRGBO(240, 240, 240, 1); +const Color _defaultGalleryThumbDrawColor = Color.fromRGBO(0, 0, 0, 0.8); + +// Default Backup Enabled Background Color +const Color _defaultBackupEnabledBgColor = Color.fromRGBO(230, 230, 230, 0.95); + +// Default Dots Indicator Colors +const Color _defaultDotsIndicatorActiveColor = Color.fromRGBO(0, 0, 0, 0.5); +const Color _defaultDotsIndicatorInactiveColor = Color.fromRGBO(0, 0, 0, 0.12); + +// Default Toast Colors +const Color _defaultToastTextColor = Color.fromRGBO(255, 255, 255, 1); +const Color _defaultToastBackgroundColor = Color.fromRGBO(24, 24, 24, 0.95); + +// Default Sub Text Color +const Color _defaultSubTextColor = Color.fromRGBO(180, 180, 180, 1); + +// Default Theme Switch Inactive Icon Color +const Color _defaultThemeSwitchInactiveIconColor = Color.fromRGBO(0, 0, 0, 0.5); + +// Default Search Results Colors +const Color _defaultSearchResultsColor = Color.fromRGBO(245, 245, 245, 1.0); +const Color _defaultMutedTextColor = Color.fromRGBO(80, 80, 80, 1); +const Color _defaultSearchResultsBackgroundColor = + Color.fromRGBO(0, 0, 0, 0.32); + +// Default Code Card Background Color +const Color _defaultCodeCardBackgroundColor = Color.fromRGBO(246, 246, 246, 1); + +/// Utility class to help apps create custom color schemes with their brand colors. +/// +/// This class provides convenient methods to generate complete color schemes +/// from a base primary color, automatically calculating the different shades +/// and variations needed for the app. +class ColorSchemeBuilder { + /// Creates light and dark color schemes from a single primary color. + /// + /// The primary color is used as the base (primary500), and other shades + /// are automatically calculated: + /// - primary700: Darker shade for emphasis + /// - primary400: Lighter shade for secondary elements + /// - primary300: Lightest shade for subtle accents + /// + /// Example: + /// ```dart + /// final schemes = ColorSchemeBuilder.fromPrimaryColor( + /// Color(0xFF2196F3), // Material Blue + /// ); + /// final lightScheme = schemes.light; + /// final darkScheme = schemes.dark; + /// ``` + static ({EnteColorScheme light, EnteColorScheme dark}) fromPrimaryColor( + Color primaryColor, + ) { + // Calculate different shades of the primary color + final HSLColor hsl = HSLColor.fromColor(primaryColor); + + final primary700 = + hsl.withLightness((hsl.lightness - 0.1).clamp(0.0, 1.0)).toColor(); + final primary500 = primaryColor; + final primary400 = + hsl.withLightness((hsl.lightness + 0.1).clamp(0.0, 1.0)).toColor(); + final primary300 = + hsl.withLightness((hsl.lightness + 0.2).clamp(0.0, 1.0)).toColor(); + + // Create gradient colors from the primary color + final gradientColors = [primary700, primary500]; + + final lightScheme = EnteColorScheme.light( + primary700: primary700, + primary500: primary500, + primary400: primary400, + primary300: primary300, + iconButtonColor: primary500, + gradientButtonBgColor: primary500, + gradientButtonBgColors: gradientColors, + ); + + final darkScheme = EnteColorScheme.dark( + primary700: primary700, + primary500: primary500, + primary400: primary400, + primary300: primary300, + iconButtonColor: primary500, + gradientButtonBgColor: primary500, + gradientButtonBgColors: gradientColors, + ); + + return (light: lightScheme, dark: darkScheme); + } + + /// Creates light and dark color schemes with fully custom primary colors. + /// + /// Use this method when you need complete control over all primary color shades. + /// + /// Example: + /// ```dart + /// final schemes = ColorSchemeBuilder.fromCustomColors( + /// primary700: Color(0xFF1565C0), + /// primary500: Color(0xFF2196F3), + /// primary400: Color(0xFF42A5F5), + /// primary300: Color(0xFF90CAF9), + /// ); + /// ``` + static ({EnteColorScheme light, EnteColorScheme dark}) fromCustomColors({ + required Color primary700, + required Color primary500, + required Color primary400, + required Color primary300, + Color? iconButtonColor, + Color? gradientButtonBgColor, + List? gradientButtonBgColors, + }) { + final effectiveIconButtonColor = iconButtonColor ?? primary500; + final effectiveGradientBgColor = gradientButtonBgColor ?? primary500; + final effectiveGradientColors = + gradientButtonBgColors ?? [primary700, primary500]; + + final lightScheme = EnteColorScheme.light( + primary700: primary700, + primary500: primary500, + primary400: primary400, + primary300: primary300, + iconButtonColor: effectiveIconButtonColor, + gradientButtonBgColor: effectiveGradientBgColor, + gradientButtonBgColors: effectiveGradientColors, + ); + + final darkScheme = EnteColorScheme.dark( + primary700: primary700, + primary500: primary500, + primary400: primary400, + primary300: primary300, + iconButtonColor: effectiveIconButtonColor, + gradientButtonBgColor: effectiveGradientBgColor, + gradientButtonBgColors: effectiveGradientColors, + ); + + return (light: lightScheme, dark: darkScheme); + } +} diff --git a/mobile/packages/ui/lib/theme/effects.dart b/mobile/packages/ui/lib/theme/effects.dart new file mode 100644 index 0000000000..f97ec06723 --- /dev/null +++ b/mobile/packages/ui/lib/theme/effects.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +const blurBase = 96.0; +const blurMuted = 48.0; +const blurFaint = 24.0; + +List shadowFloatLight = const [ + BoxShadow(blurRadius: 10, color: Color.fromRGBO(0, 0, 0, 0.25)), +]; + +List shadowFloatFaintLight = const [ + BoxShadow(blurRadius: 10, color: Color.fromRGBO(0, 0, 0, 0.12)), +]; + +List shadowFloatFaintestLight = const [ + BoxShadow(blurRadius: 1, color: Color.fromRGBO(0, 0, 0, 0.25)), +]; + +List shadowMenuLight = const [ + BoxShadow(blurRadius: 6, color: Color.fromRGBO(0, 0, 0, 0.16)), + BoxShadow( + blurRadius: 6, + color: Color.fromRGBO(0, 0, 0, 0.12), + offset: Offset(0, 3), + ), +]; + +List shadowButtonLight = const [ + BoxShadow( + blurRadius: 4, + color: Color.fromRGBO(0, 0, 0, 0.25), + offset: Offset(0, 4), + ), +]; + +List shadowFloatDark = const [ + BoxShadow( + blurRadius: 12, + color: Color.fromRGBO(0, 0, 0, 0.75), + offset: Offset(0, 2), + ), +]; + +List shadowMenuDark = const [ + BoxShadow(blurRadius: 6, color: Color.fromRGBO(0, 0, 0, 0.50)), + BoxShadow( + blurRadius: 6, + color: Color.fromRGBO(0, 0, 0, 0.25), + offset: Offset(0, 3), + ), +]; + +List shadowButtonDark = const [ + BoxShadow( + blurRadius: 4, + color: Color.fromRGBO(0, 0, 0, 0.75), + offset: Offset(0, 4), + ), +]; diff --git a/mobile/packages/ui/lib/theme/ente_theme.dart b/mobile/packages/ui/lib/theme/ente_theme.dart new file mode 100644 index 0000000000..25e16e705f --- /dev/null +++ b/mobile/packages/ui/lib/theme/ente_theme.dart @@ -0,0 +1,85 @@ +import 'package:flutter/material.dart'; +import 'colors.dart'; +import 'effects.dart'; +import 'text_style.dart'; + +class EnteTheme { + final EnteTextTheme textTheme; + final EnteColorScheme colorScheme; + final List shadowFloat; + final List shadowMenu; + final List shadowButton; + + const EnteTheme( + this.textTheme, + this.colorScheme, { + required this.shadowFloat, + required this.shadowMenu, + required this.shadowButton, + }); + + bool isDark(BuildContext context) { + return Theme.of(context).brightness == Brightness.dark; + } +} + +EnteTheme lightTheme = EnteTheme( + lightTextTheme, + lightScheme, + shadowFloat: shadowFloatLight, + shadowMenu: shadowMenuLight, + shadowButton: shadowButtonLight, +); + +EnteTheme darkTheme = EnteTheme( + darkTextTheme, + darkScheme, + shadowFloat: shadowFloatDark, + shadowMenu: shadowMenuDark, + shadowButton: shadowButtonDark, +); + +EnteColorScheme getEnteColorScheme( + BuildContext context, { + bool inverse = false, +}) { + final colorScheme = Theme.of(context).extension(); + if (colorScheme != null) { + return colorScheme; + } + + // Fallback to old system if new system is not available + return inverse + ? getEnteColorScheme(context).inverseEnteTheme.colorScheme + : getEnteColorScheme(context); +} + +EnteTextTheme getEnteTextTheme( + BuildContext context, { + bool inverse = false, +}) { + final isDark = Theme.of(context).brightness == Brightness.dark; + if (inverse) { + return isDark ? lightTextTheme : darkTextTheme; + } else { + return isDark ? darkTextTheme : lightTextTheme; + } +} + +/// Get theme-aware shadow for floating elements (dialogs, modals, etc.) +List getEnteShadowFloat(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + return isDark ? shadowFloatDark : shadowFloatLight; +} + +/// Get theme-aware shadow for menu elements +List getEnteShadowMenu(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + return isDark ? shadowMenuDark : shadowMenuLight; +} + +/// Get theme-aware shadow for button elements +List getEnteShadowButton(BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; + return isDark ? shadowButtonDark : shadowButtonLight; +} diff --git a/mobile/packages/ui/lib/theme/ente_theme_data.dart b/mobile/packages/ui/lib/theme/ente_theme_data.dart new file mode 100644 index 0000000000..8e2448feb1 --- /dev/null +++ b/mobile/packages/ui/lib/theme/ente_theme_data.dart @@ -0,0 +1,537 @@ +import '../theme/colors.dart'; +import 'package:flutter/material.dart'; +import '../theme/ente_theme.dart'; + +final lightThemeData = ThemeData( + fontFamily: 'Inter', + brightness: Brightness.light, + dividerTheme: const DividerThemeData( + color: Colors.black12, + ), + hintColor: const Color.fromRGBO(158, 158, 158, 1), + primaryColor: const Color.fromRGBO(255, 110, 64, 1), + primaryColorLight: const Color.fromRGBO(0, 0, 0, 0.541), + iconTheme: const IconThemeData(color: Colors.black), + primaryIconTheme: + const IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0), + buttonTheme: const ButtonThemeData(), + outlinedButtonTheme: buildOutlinedButtonThemeData( + bgDisabled: const Color.fromRGBO(158, 158, 158, 1), + bgEnabled: const Color.fromRGBO(0, 0, 0, 1), + fgDisabled: const Color.fromRGBO(255, 255, 255, 1), + fgEnabled: const Color.fromRGBO(255, 255, 255, 1), + ), + elevatedButtonTheme: buildElevatedButtonThemeData( + onPrimary: const Color.fromRGBO(255, 255, 255, 1), + primary: const Color.fromRGBO(0, 0, 0, 1), + ), + scaffoldBackgroundColor: const Color.fromRGBO(255, 255, 255, 1), + appBarTheme: const AppBarTheme().copyWith( + backgroundColor: Colors.white, + foregroundColor: Colors.black, + iconTheme: const IconThemeData(color: Colors.black), + elevation: 0, + ), + //https://api.flutter.dev/flutter/material/TextTheme-class.html + textTheme: _buildTextTheme(const Color.fromRGBO(0, 0, 0, 1)), + primaryTextTheme: const TextTheme().copyWith( + bodyMedium: const TextStyle(color: Colors.yellow), + bodyLarge: const TextStyle(color: Colors.orange), + ), + cardColor: const Color.fromRGBO(250, 250, 250, 1.0), + dialogTheme: const DialogThemeData().copyWith( + backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), // + titleTextStyle: const TextStyle( + color: Colors.black, + fontSize: 24, + fontWeight: FontWeight.w600, + ), + contentTextStyle: const TextStyle( + fontFamily: 'Inter-Medium', + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + checkboxTheme: CheckboxThemeData( + side: const BorderSide( + color: Colors.black, + width: 2, + ), + fillColor: WidgetStateProperty.resolveWith((states) { + return states.contains(WidgetState.selected) + ? const Color.fromRGBO(0, 0, 0, 1) + : const Color.fromRGBO(255, 255, 255, 1); + }), + checkColor: WidgetStateProperty.resolveWith((states) { + return states.contains(WidgetState.selected) + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(0, 0, 0, 1); + }), + ), + + radioTheme: RadioThemeData( + fillColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + ), + switchTheme: SwitchThemeData( + thumbColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + trackColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + ), + colorScheme: const ColorScheme.light( + primary: Colors.black, + secondary: Color.fromARGB(255, 163, 163, 163), + ).copyWith(surface: const Color.fromRGBO(255, 255, 255, 1)), +); + +final darkThemeData = ThemeData( + fontFamily: 'Inter', + brightness: Brightness.dark, + dividerTheme: const DividerThemeData( + color: Colors.white12, + ), + primaryColorLight: const Color.fromRGBO(255, 255, 255, 0.702), + iconTheme: const IconThemeData(color: Colors.white), + primaryIconTheme: + const IconThemeData(color: Colors.red, opacity: 1.0, size: 50.0), + hintColor: const Color.fromRGBO(158, 158, 158, 1), + buttonTheme: const ButtonThemeData().copyWith( + buttonColor: const Color.fromRGBO(45, 194, 98, 1.0), + height: 56, + ), + textTheme: _buildTextTheme(const Color.fromRGBO(255, 255, 255, 1)), + outlinedButtonTheme: buildOutlinedButtonThemeData( + bgDisabled: const Color.fromRGBO(158, 158, 158, 1), + bgEnabled: const Color.fromRGBO(255, 255, 255, 1), + fgDisabled: const Color.fromRGBO(255, 255, 255, 1), + fgEnabled: const Color.fromRGBO(0, 0, 0, 1), + ), + elevatedButtonTheme: buildElevatedButtonThemeData( + onPrimary: const Color.fromRGBO(0, 0, 0, 1), + primary: const Color.fromRGBO(255, 255, 255, 1), + ), + scaffoldBackgroundColor: const Color.fromRGBO(0, 0, 0, 1), + appBarTheme: const AppBarTheme().copyWith( + color: Colors.black, + elevation: 0, + ), + cardColor: const Color.fromRGBO(10, 15, 15, 1.0), + dialogTheme: const DialogThemeData().copyWith( + backgroundColor: const Color.fromRGBO(15, 15, 15, 1.0), + titleTextStyle: const TextStyle( + color: Colors.white, + fontSize: 24, + fontWeight: FontWeight.w600, + ), + contentTextStyle: const TextStyle( + fontFamily: 'Inter-Medium', + color: Colors.white, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + checkboxTheme: CheckboxThemeData( + side: const BorderSide( + color: Colors.grey, + width: 2, + ), + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(158, 158, 158, 1); + } else { + return const Color.fromRGBO(0, 0, 0, 1); + } + }), + checkColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(0, 0, 0, 1); + } else { + return const Color.fromRGBO(158, 158, 158, 1); + } + }), + ), + radioTheme: RadioThemeData( + fillColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + ), + switchTheme: SwitchThemeData( + thumbColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + trackColor: + WidgetStateProperty.resolveWith((Set states) { + if (states.contains(WidgetState.disabled)) { + return null; + } + if (states.contains(WidgetState.selected)) { + return const Color.fromRGBO(102, 187, 106, 1); + } + return null; + }), + ), + colorScheme: const ColorScheme.dark(primary: Colors.white) + .copyWith(surface: const Color.fromRGBO(0, 0, 0, 1)), +); + +TextTheme _buildTextTheme(Color textColor) { + return const TextTheme().copyWith( + headlineMedium: TextStyle( + color: textColor, + fontSize: 32, + fontWeight: FontWeight.w600, + fontFamily: 'Inter', + ), + headlineSmall: TextStyle( + color: textColor, + fontSize: 24, + fontWeight: FontWeight.w600, + fontFamily: 'Inter', + ), + titleLarge: TextStyle( + color: textColor, + fontSize: 18, + fontFamily: 'Inter', + fontWeight: FontWeight.w600, + ), + titleMedium: TextStyle( + color: textColor, + fontFamily: 'Inter', + fontSize: 16, + fontWeight: FontWeight.w500, + ), + titleSmall: TextStyle( + color: textColor, + fontFamily: 'Inter', + fontSize: 14, + fontWeight: FontWeight.w500, + ), + bodyLarge: TextStyle( + fontFamily: 'Inter', + color: textColor, + fontSize: 16, + fontWeight: FontWeight.w500, + ), + bodyMedium: TextStyle( + fontFamily: 'Inter', + color: textColor, + fontSize: 14, + fontWeight: FontWeight.w500, + ), + bodySmall: TextStyle( + color: textColor.withOpacity(0.4), + fontSize: 10, + fontWeight: FontWeight.w500, + ), + labelSmall: TextStyle( + fontFamily: 'Inter', + color: textColor, + fontSize: 14, + fontWeight: FontWeight.w500, + decoration: TextDecoration.underline, + ), + ); +} + +extension CustomColorScheme on ColorScheme { + Color get defaultBackgroundColor => + brightness == Brightness.light ? backgroundBaseLight : backgroundBaseDark; + + Color get inverseBackgroundColor => + brightness != Brightness.light ? backgroundBaseLight : backgroundBaseDark; + + Color get fabForegroundColor => brightness == Brightness.light + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(40, 40, 40, 1); + + Color get fabBackgroundColor => brightness != Brightness.light + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(40, 40, 40, 1); + + Color get defaultTextColor => + brightness == Brightness.light ? textBaseLight : textBaseDark; + + Color get inverseTextColor => + brightness != Brightness.light ? textBaseLight : textBaseDark; + + Color get boxSelectColor => brightness == Brightness.light + ? const Color.fromRGBO(67, 186, 108, 1) + : const Color.fromRGBO(16, 32, 32, 1); + + Color get boxUnSelectColor => brightness == Brightness.light + ? const Color.fromRGBO(240, 240, 240, 1) + : const Color.fromRGBO(8, 18, 18, 0.4); + + Color get alternativeColor => const Color.fromARGB(255, 152, 77, 244); + + Color get dynamicFABBackgroundColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1) + : const Color.fromRGBO(48, 48, 48, 1); + + Color get dynamicFABTextColor => + const Color.fromRGBO(255, 255, 255, 1); //same for both themes + + // todo: use brightness == Brightness.light for changing color for dark/light + // theme + ButtonStyle? get optionalActionButtonStyle => buildElevatedButtonThemeData( + onPrimary: const Color(0xFF777777), + primary: const Color(0xFFF0F0F0), + elevation: 0, + ).style; + + Color get recoveryKeyBoxColor => brightness == Brightness.light + ? const Color.fromARGB(51, 150, 0, 220) + : const Color.fromARGB(255, 174, 56, 247); + + Color get frostyBlurBackdropFilterColor => brightness == Brightness.light + ? const Color.fromRGBO(238, 238, 238, 0.5) + : const Color.fromRGBO(48, 48, 48, 0.5); + + Color get iconColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.75) + : const Color.fromRGBO(255, 255, 255, 1); + + Color get bgColorForQuestions => brightness == Brightness.light + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(10, 15, 15, 1.0); + + Color get greenText => const Color.fromARGB(255, 40, 190, 113); + + Color get cupertinoPickerTopColor => brightness == Brightness.light + ? const Color.fromARGB(255, 238, 238, 238) + : const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.1); + + Color get stepProgressUnselectedColor => brightness == Brightness.light + ? const Color.fromRGBO(196, 196, 196, 0.6) + : const Color.fromRGBO(255, 255, 255, 0.7); + + Color get gNavBackgroundColor => brightness == Brightness.light + ? const Color.fromRGBO(196, 196, 196, 0.6) + : const Color.fromRGBO(40, 40, 40, 0.6); + + Color get gNavBarActiveColor => brightness == Brightness.light + ? const Color.fromRGBO(255, 255, 255, 0.6) + : const Color.fromRGBO(255, 255, 255, 0.9); + + Color get gNavIconColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 0.8) + : const Color.fromRGBO(255, 255, 255, 0.8); + + Color get gNavActiveIconColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 0.8) + : const Color.fromRGBO(0, 0, 0, 0.8); + + Color get galleryThumbBackgroundColor => brightness == Brightness.light + ? const Color.fromRGBO(240, 240, 240, 1) + : const Color.fromRGBO(20, 20, 20, 1); + + Color get galleryThumbDrawColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.8) + : const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5); + + Color get backupEnabledBgColor => brightness == Brightness.light + ? const Color.fromRGBO(230, 230, 230, 0.95) + : const Color.fromRGBO(10, 40, 40, 0.3); + + Color get dotsIndicatorActiveColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.5) + : const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5); + + Color get dotsIndicatorInactiveColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.12) + : const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.12); + + Color get toastTextColor => brightness == Brightness.light + ? const Color.fromRGBO(255, 255, 255, 1) + : const Color.fromRGBO(0, 0, 0, 1); + + Color get toastBackgroundColor => brightness == Brightness.light + ? const Color.fromRGBO(24, 24, 24, 0.95) + : const Color.fromRGBO(255, 255, 255, 0.95); + + Color get subTextColor => brightness == Brightness.light + ? const Color.fromRGBO(180, 180, 180, 1) + : const Color.fromRGBO(100, 100, 100, 1); + + Color get themeSwitchInactiveIconColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 1).withOpacity(0.5) + : const Color.fromRGBO(255, 255, 255, 1).withOpacity(0.5); + + Color get searchResultsColor => brightness == Brightness.light + ? const Color.fromRGBO(245, 245, 245, 1.0) + : const Color.fromRGBO(30, 30, 30, 1.0); + + Color get mutedTextColor => brightness == Brightness.light + ? const Color.fromRGBO(80, 80, 80, 1) + : const Color.fromRGBO(150, 150, 150, 1); + + Color get searchResultsBackgroundColor => brightness == Brightness.light + ? Colors.black.withOpacity(0.32) + : Colors.black.withOpacity(0.64); + + Color get codeCardBackgroundColor => brightness == Brightness.light + ? const Color.fromRGBO(246, 246, 246, 1) + : const Color.fromRGBO(40, 40, 40, 0.6); + + Color get primaryColor => brightness == Brightness.light + ? const Color(0xFF9610D6) + : const Color(0xFF9610D6); + + EnteTheme get enteTheme => + brightness == Brightness.light ? lightTheme : darkTheme; + + EnteTheme get inverseEnteTheme => + brightness == Brightness.light ? darkTheme : lightTheme; +} + +OutlinedButtonThemeData buildOutlinedButtonThemeData({ + required Color bgDisabled, + required Color bgEnabled, + required Color fgDisabled, + required Color fgEnabled, +}) { + return OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + fixedSize: const Size.fromHeight(56), + alignment: Alignment.center, + padding: const EdgeInsets.fromLTRB(50, 16, 50, 16), + textStyle: const TextStyle( + fontWeight: FontWeight.w600, + fontFamily: 'Inter-SemiBold', + fontSize: 18, + ), + ).copyWith( + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) { + if (states.contains(WidgetState.disabled)) { + return bgDisabled; + } + return bgEnabled; + }, + ), + foregroundColor: WidgetStateProperty.resolveWith( + (Set states) { + if (states.contains(WidgetState.disabled)) { + return fgDisabled; + } + return fgEnabled; + }, + ), + alignment: Alignment.center, + ), + ); +} + +ElevatedButtonThemeData buildElevatedButtonThemeData({ + required Color onPrimary, // text button color + required Color primary, + double elevation = 2, // background color of button +}) { + return ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + foregroundColor: onPrimary, + backgroundColor: primary, + elevation: elevation, + alignment: Alignment.center, + textStyle: const TextStyle( + fontWeight: FontWeight.w600, + fontFamily: 'Inter-SemiBold', + fontSize: 18, + ), + padding: const EdgeInsets.symmetric(vertical: 8), + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(4)), + ), + ), + ); +} + +// Helper function to create ThemeData that works with the new color system +ThemeData createAppThemeData({ + required Brightness brightness, + EnteColorScheme? colorScheme, +}) { + final effectiveColorScheme = colorScheme ?? + (brightness == Brightness.light ? lightScheme : darkScheme); + + final baseThemeData = + brightness == Brightness.light ? lightThemeData : darkThemeData; + + // Create platform-specific typography to ensure consistent font sizes + final typography = Typography.material2021( + platform: TargetPlatform.android, // Force Android typography for consistency + ); + + return baseThemeData.copyWith( + extensions: [effectiveColorScheme], + primaryColor: effectiveColorScheme.primary500, + scaffoldBackgroundColor: effectiveColorScheme.backgroundBase, + typography: typography, + dialogTheme: baseThemeData.dialogTheme.copyWith( + backgroundColor: effectiveColorScheme.backgroundElevated, + ), + appBarTheme: baseThemeData.appBarTheme.copyWith( + backgroundColor: effectiveColorScheme.backgroundBase, + foregroundColor: effectiveColorScheme.textBase, + iconTheme: IconThemeData(color: effectiveColorScheme.textBase), + ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + backgroundColor: effectiveColorScheme.primary500, + foregroundColor: effectiveColorScheme.backgroundBase, + ), + ), + outlinedButtonTheme: OutlinedButtonThemeData( + style: OutlinedButton.styleFrom( + backgroundColor: effectiveColorScheme.fillFaint, + foregroundColor: effectiveColorScheme.textBase, + side: BorderSide(color: effectiveColorScheme.strokeMuted), + ), + ), + ); +} diff --git a/mobile/packages/ui/lib/theme/example_app_colors.dart b/mobile/packages/ui/lib/theme/example_app_colors.dart new file mode 100644 index 0000000000..22ee8b99f8 --- /dev/null +++ b/mobile/packages/ui/lib/theme/example_app_colors.dart @@ -0,0 +1,152 @@ +// Example: How to use the reusable EnteColorScheme in your app +// filepath: example_app_colors.dart + +import 'package:flutter/material.dart'; +import 'colors.dart'; // Import the reusable color scheme +import 'ente_theme_data.dart'; // Import the theme data helper +import 'ente_theme.dart'; // Import for getEnteColorScheme + +/// Example 1: Using the default color scheme +class DefaultThemeExample { + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: lightScheme, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: darkScheme, + ); +} + +/// Example 2: Creating a custom theme with brand colors +class CustomBrandThemeExample { + // Define your app's brand colors + static const Color brandPrimaryColor = Color(0xFF6C5CE7); // Purple + + static final schemes = ColorSchemeBuilder.fromPrimaryColor(brandPrimaryColor); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); +} + +/// Example 3: Creating a theme with fully custom primary colors +class FullyCustomThemeExample { + static final schemes = ColorSchemeBuilder.fromCustomColors( + primary700: const Color(0xFF1565C0), // Dark blue + primary500: const Color(0xFF2196F3), // Material blue + primary400: const Color(0xFF42A5F5), // Light blue + primary300: const Color(0xFF90CAF9), // Very light blue + iconButtonColor: const Color(0xFF1976D2), // Custom icon color + gradientButtonBgColors: const [ + Color(0xFF1565C0), + Color(0xFF2196F3), + Color(0xFF42A5F5), + ], + ); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); +} + +/// Example 4: Using factory constructors for fine-grained control +class FactoryConstructorExample { + static final lightScheme = EnteColorScheme.light( + primary700: const Color(0xFFE91E63), // Pink 700 + primary500: const Color(0xFFF06292), // Pink 300 + primary400: const Color(0xFFF8BBD9), // Pink 200 + primary300: const Color(0xFFFCE4EC), // Pink 50 + warning500: const Color(0xFFFF5722), // Custom warning color + ); + + static final darkScheme = EnteColorScheme.dark( + primary700: const Color(0xFFE91E63), + primary500: const Color(0xFFF06292), + primary400: const Color(0xFFF8BBD9), + primary300: const Color(0xFFFCE4EC), + warning500: const Color(0xFFFF5722), + ); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: lightScheme, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: darkScheme, + ); +} + +/// Helper function to get the current color scheme from context +EnteColorScheme getColorScheme(BuildContext context) { + return getEnteColorScheme(context); +} + +/// Example widget showing how to use the color scheme in your UI +class ExampleWidget extends StatelessWidget { + const ExampleWidget({super.key}); + + @override + Widget build(BuildContext context) { + final colorScheme = getColorScheme(context); + + return Container( + color: colorScheme.backgroundBase, + child: Column( + children: [ + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.backgroundElevated, + border: Border.all(color: colorScheme.strokeFaint), + ), + child: Text( + 'Example Text', + style: TextStyle(color: colorScheme.textBase), + ), + ), + ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.primary500, + foregroundColor: colorScheme.backgroundBase, + ), + onPressed: () {}, + child: const Text('Primary Button'), + ), + Container( + height: 50, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: colorScheme.gradientButtonBgColors, + ), + ), + child: Center( + child: Text( + 'Gradient Button', + style: TextStyle( + color: colorScheme.backgroundBase, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/packages/ui/lib/theme/multi_app_demo.dart b/mobile/packages/ui/lib/theme/multi_app_demo.dart new file mode 100644 index 0000000000..939cbe81bb --- /dev/null +++ b/mobile/packages/ui/lib/theme/multi_app_demo.dart @@ -0,0 +1,427 @@ +// Demo: Complete working example showing multi-app theme compatibility +// This file demonstrates how the reusable theme system works for different apps + +import 'package:flutter/material.dart'; +import 'colors.dart'; +import 'ente_theme_data.dart'; +import 'ente_theme.dart'; + +/// App 1: E-commerce app with blue theme +class ECommerceApp { + static const Color brandBlue = Color(0xFF1976D2); + + static final schemes = ColorSchemeBuilder.fromPrimaryColor(brandBlue); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); +} + +/// App 2: Social media app with purple theme +class SocialMediaApp { + static const Color brandPurple = Color(0xFF9C27B0); + + static final schemes = ColorSchemeBuilder.fromPrimaryColor(brandPurple); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); +} + +/// App 3: Finance app with green theme +class FinanceApp { + static final schemes = ColorSchemeBuilder.fromCustomColors( + primary700: const Color(0xFF388E3C), + primary500: const Color(0xFF4CAF50), + primary400: const Color(0xFF66BB6A), + primary300: const Color(0xFF81C784), + gradientButtonBgColors: const [ + Color(0xFF388E3C), + Color(0xFF4CAF50), + Color(0xFF66BB6A), + ], + ); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); +} + +/// App 4: Gaming app with orange theme +class GamingApp { + static final customLightScheme = EnteColorScheme.light( + primary700: const Color(0xFFE65100), + primary500: const Color(0xFFFF9800), + primary400: const Color(0xFFFFB74D), + primary300: const Color(0xFFFFCC02), + iconButtonColor: const Color(0xFFFF6F00), + gradientButtonBgColors: const [ + Color(0xFFE65100), + Color(0xFFFF9800), + Color(0xFFFFB74D), + ], + warning500: const Color(0xFFF44336), // Custom warning for gaming + ); + + static final customDarkScheme = EnteColorScheme.dark( + primary700: const Color(0xFFE65100), + primary500: const Color(0xFFFF9800), + primary400: const Color(0xFFFFB74D), + primary300: const Color(0xFFFFCC02), + iconButtonColor: const Color(0xFFFF6F00), + gradientButtonBgColors: const [ + Color(0xFFE65100), + Color(0xFFFF9800), + Color(0xFFFFB74D), + ], + warning500: const Color(0xFFF44336), + ); + + static final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: customLightScheme, + ); + + static final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: customDarkScheme, + ); +} + +/// Demo widget that shows how UI components adapt to different app themes +class MultiAppThemeDemo extends StatefulWidget { + const MultiAppThemeDemo({super.key}); + + @override + State createState() => _MultiAppThemeDemoState(); +} + +class _MultiAppThemeDemoState extends State { + int currentAppIndex = 0; + bool isDarkMode = false; + + final List<({String name, ThemeData light, ThemeData dark})> apps = [ + ( + name: "E-commerce", + light: ECommerceApp.lightTheme, + dark: ECommerceApp.darkTheme + ), + ( + name: "Social Media", + light: SocialMediaApp.lightTheme, + dark: SocialMediaApp.darkTheme + ), + (name: "Finance", light: FinanceApp.lightTheme, dark: FinanceApp.darkTheme), + (name: "Gaming", light: GamingApp.lightTheme, dark: GamingApp.darkTheme), + ]; + + @override + Widget build(BuildContext context) { + final currentApp = apps[currentAppIndex]; + final currentTheme = isDarkMode ? currentApp.dark : currentApp.light; + + return MaterialApp( + title: '${currentApp.name} App Demo', + theme: currentTheme, + home: DemoHomePage( + appName: currentApp.name, + onAppChanged: (index) => setState(() => currentAppIndex = index), + onThemeChanged: (dark) => setState(() => isDarkMode = dark), + currentAppIndex: currentAppIndex, + isDarkMode: isDarkMode, + appCount: apps.length, + ), + ); + } +} + +class DemoHomePage extends StatelessWidget { + final String appName; + final Function(int) onAppChanged; + final Function(bool) onThemeChanged; + final int currentAppIndex; + final bool isDarkMode; + final int appCount; + + const DemoHomePage({ + super.key, + required this.appName, + required this.onAppChanged, + required this.onThemeChanged, + required this.currentAppIndex, + required this.isDarkMode, + required this.appCount, + }); + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + + return Scaffold( + backgroundColor: colorScheme.backgroundBase, + appBar: AppBar( + title: Text('$appName Theme Demo'), + backgroundColor: colorScheme.backgroundElevated, + foregroundColor: colorScheme.textBase, + actions: [ + Switch( + value: isDarkMode, + onChanged: onThemeChanged, + activeColor: colorScheme.primary500, + ), + ], + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // App Selector + Text( + 'Switch App Theme:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 12), + Wrap( + spacing: 8, + children: List.generate(appCount, (index) { + final isSelected = index == currentAppIndex; + return FilterChip( + label: Text( + ['E-commerce', 'Social', 'Finance', 'Gaming'][index]), + selected: isSelected, + onSelected: (_) => onAppChanged(index), + backgroundColor: colorScheme.fillFaint, + selectedColor: colorScheme.primary400, + labelStyle: TextStyle( + color: isSelected + ? colorScheme.backgroundBase + : colorScheme.textBase, + ), + ); + }), + ), + const SizedBox(height: 32), + + // UI Components Demo + Text( + 'UI Components:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 16), + + // Background colors demo + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.backgroundElevated, + border: Border.all(color: colorScheme.strokeFaint), + borderRadius: BorderRadius.circular(8), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Card with elevated background', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 8), + Text( + 'This is secondary text that adapts to the theme.', + style: TextStyle(color: colorScheme.textMuted), + ), + Text( + 'This is faint text for hints and labels.', + style: TextStyle(color: colorScheme.textFaint), + ), + ], + ), + ), + const SizedBox(height: 16), + + // Buttons demo + Row( + children: [ + Expanded( + child: ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: colorScheme.primary500, + foregroundColor: colorScheme.backgroundBase, + ), + onPressed: () {}, + child: const Text('Primary Button'), + ), + ), + const SizedBox(width: 12), + Expanded( + child: OutlinedButton( + style: OutlinedButton.styleFrom( + backgroundColor: colorScheme.fillFaint, + foregroundColor: colorScheme.textBase, + side: BorderSide(color: colorScheme.strokeMuted), + ), + onPressed: () {}, + child: const Text('Secondary'), + ), + ), + ], + ), + const SizedBox(height: 16), + + // Gradient button demo + Container( + width: double.infinity, + height: 48, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: colorScheme.gradientButtonBgColors, + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ), + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Text( + 'Gradient Button', + style: TextStyle( + color: colorScheme.backgroundBase, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + ), + ), + ), + const SizedBox(height: 16), + + // Warning demo + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.warning500.withOpacity(0.1), + border: Border.all(color: colorScheme.warning500), + borderRadius: BorderRadius.circular(8), + ), + child: Row( + children: [ + Icon( + Icons.warning, + color: colorScheme.warning500, + size: 20, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Warning message with custom warning color', + style: TextStyle(color: colorScheme.warning700), + ), + ), + ], + ), + ), + const SizedBox(height: 32), + + // Color palette display + Text( + 'Color Palette:', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 16), + _buildColorPalette(colorScheme), + ], + ), + ), + floatingActionButton: FloatingActionButton( + backgroundColor: colorScheme.iconButtonColor, + foregroundColor: colorScheme.backgroundBase, + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('$appName theme is working!'), + backgroundColor: colorScheme.primary500, + ), + ); + }, + child: const Icon(Icons.palette), + ), + ); + } + + Widget _buildColorPalette(EnteColorScheme colorScheme) { + final colors = [ + ('Primary 700', colorScheme.primary700), + ('Primary 500', colorScheme.primary500), + ('Primary 400', colorScheme.primary400), + ('Primary 300', colorScheme.primary300), + ('Warning', colorScheme.warning500), + ('Icon Button', colorScheme.iconButtonColor), + ]; + + return Wrap( + spacing: 8, + runSpacing: 8, + children: colors.map((color) { + return Column( + children: [ + Container( + width: 60, + height: 60, + decoration: BoxDecoration( + color: color.$2, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.strokeFaint), + ), + ), + const SizedBox(height: 4), + Text( + color.$1, + style: TextStyle( + fontSize: 10, + color: colorScheme.textMuted, + ), + textAlign: TextAlign.center, + ), + ], + ); + }).toList(), + ); + } +} + +// Example of how to use this in main.dart: +void main() { + runApp(const MultiAppThemeDemo()); +} diff --git a/mobile/packages/ui/lib/theme/platform_text_config.dart b/mobile/packages/ui/lib/theme/platform_text_config.dart new file mode 100644 index 0000000000..ffe476e40d --- /dev/null +++ b/mobile/packages/ui/lib/theme/platform_text_config.dart @@ -0,0 +1,75 @@ +import 'dart:io'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +/// Platform-specific text scaling configuration to ensure consistent +/// font sizes and appearance across Android and iOS platforms. +class PlatformTextConfig { + /// Android tends to render fonts slightly larger than iOS, so we apply + /// a small reduction factor to maintain visual consistency. + static const double androidFontScaleFactor = 0.95; + + /// iOS uses the default scaling (1.0) + static const double iosFontScaleFactor = 1.0; + + /// Get the appropriate font scale factor for the current platform + static double getPlatformFontScaleFactor() { + if (kIsWeb) return 1.0; + + switch (Platform.operatingSystem) { + case 'android': + return androidFontScaleFactor; + case 'ios': + return iosFontScaleFactor; + default: + return 1.0; + } + } + + /// Adjust font size based on platform to ensure consistency + static double adjustFontSize(double baseFontSize) { + return baseFontSize * getPlatformFontScaleFactor(); + } + + /// Create a TextStyle with platform-adjusted font size + static TextStyle createTextStyle({ + required double fontSize, + FontWeight? fontWeight, + String? fontFamily, + Color? color, + double? height, + TextDecoration? decoration, + }) { + return TextStyle( + fontSize: adjustFontSize(fontSize), + fontWeight: fontWeight, + fontFamily: fontFamily, + color: color, + height: height, + decoration: decoration, + ); + } + + /// Get platform-specific MediaQuery configuration for text scaling + static MediaQueryData adjustMediaQueryTextScaling(MediaQueryData data) { + // Clamp text scaling between 0.8 and 1.3 to prevent extreme scaling + // that can break UI layouts + final textScaleFactor = + (data.textScaler.scale(1.0) * getPlatformFontScaleFactor()) + .clamp(0.8, 1.3); + + return data.copyWith( + textScaler: TextScaler.linear(textScaleFactor), + ); + } +} + +/// Extension on BuildContext to easily access platform-adjusted text scaling +extension PlatformTextScaling on BuildContext { + /// Get MediaQuery with platform-adjusted text scaling + MediaQueryData get platformAdjustedMediaQuery { + return PlatformTextConfig.adjustMediaQueryTextScaling( + MediaQuery.of(this), + ); + } +} diff --git a/mobile/packages/ui/lib/theme/text_style.dart b/mobile/packages/ui/lib/theme/text_style.dart new file mode 100644 index 0000000000..d6b69ca83f --- /dev/null +++ b/mobile/packages/ui/lib/theme/text_style.dart @@ -0,0 +1,204 @@ +import 'package:flutter/material.dart'; +import '../theme/colors.dart'; +import '../theme/platform_text_config.dart'; + +const FontWeight _regularWeight = FontWeight.w500; +const FontWeight _boldWeight = FontWeight.w600; +const String _fontFamily = 'Inter'; + +final TextStyle brandStyleSmall = TextStyle( + fontWeight: FontWeight.bold, + fontFamily: 'Montserrat', + fontSize: PlatformTextConfig.adjustFontSize(21), +); + +final TextStyle brandStyleMedium = TextStyle( + fontWeight: FontWeight.bold, + fontFamily: 'Montserrat', + fontSize: PlatformTextConfig.adjustFontSize(24), +); + +final TextStyle h1 = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(48), + height: 48 / 28, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle h2 = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(32), + height: 39 / 32.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle h3 = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(24), + height: 29 / 24.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle large = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(18), + height: 22 / 18.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle body = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(16), + height: 20 / 16.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle small = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(14), + height: 17 / 14.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle mini = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(12), + height: 15 / 12.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); +final TextStyle tiny = TextStyle( + fontSize: PlatformTextConfig.adjustFontSize(10), + height: 12 / 10.0, + fontWeight: _regularWeight, + fontFamily: _fontFamily, +); + +class EnteTextTheme { + final TextStyle h1; + final TextStyle h1Bold; + final TextStyle h2; + final TextStyle h2Bold; + final TextStyle h3; + final TextStyle h3Bold; + final TextStyle large; + final TextStyle largeBold; + final TextStyle body; + final TextStyle bodyBold; + final TextStyle small; + final TextStyle smallBold; + final TextStyle mini; + final TextStyle miniBold; + final TextStyle tiny; + final TextStyle tinyBold; + final TextStyle brandSmall; + final TextStyle brandMedium; + + // textMuted variants + final TextStyle h1Muted; + final TextStyle h2Muted; + final TextStyle h3Muted; + final TextStyle largeMuted; + final TextStyle bodyMuted; + final TextStyle smallMuted; + final TextStyle miniMuted; + final TextStyle miniBoldMuted; + final TextStyle tinyMuted; + + // textFaint variants + final TextStyle h1Faint; + final TextStyle h2Faint; + final TextStyle h3Faint; + final TextStyle largeFaint; + final TextStyle bodyFaint; + final TextStyle smallFaint; + final TextStyle miniFaint; + final TextStyle tinyFaint; + + const EnteTextTheme({ + required this.h1, + required this.h1Bold, + required this.h2, + required this.h2Bold, + required this.h3, + required this.h3Bold, + required this.large, + required this.largeBold, + required this.body, + required this.bodyBold, + required this.small, + required this.smallBold, + required this.mini, + required this.miniBold, + required this.tiny, + required this.tinyBold, + required this.brandSmall, + required this.brandMedium, + required this.h1Muted, + required this.h2Muted, + required this.h3Muted, + required this.largeMuted, + required this.bodyMuted, + required this.smallMuted, + required this.miniMuted, + required this.miniBoldMuted, + required this.tinyMuted, + required this.h1Faint, + required this.h2Faint, + required this.h3Faint, + required this.largeFaint, + required this.bodyFaint, + required this.smallFaint, + required this.miniFaint, + required this.tinyFaint, + }); +} + +EnteTextTheme lightTextTheme = _buildEnteTextStyle( + textBaseLight, + textMutedLight, + textFaintLight, +); + +EnteTextTheme darkTextTheme = _buildEnteTextStyle( + textBaseDark, + textMutedDark, + textFaintDark, +); + +EnteTextTheme _buildEnteTextStyle( + Color color, + Color textMuted, + Color textFaint, +) { + return EnteTextTheme( + h1: h1.copyWith(color: color), + h1Bold: h1.copyWith(color: color, fontWeight: _boldWeight), + h2: h2.copyWith(color: color), + h2Bold: h2.copyWith(color: color, fontWeight: _boldWeight), + h3: h3.copyWith(color: color), + h3Bold: h3.copyWith(color: color, fontWeight: _boldWeight), + large: large.copyWith(color: color), + largeBold: large.copyWith(color: color, fontWeight: _boldWeight), + body: body.copyWith(color: color), + bodyBold: body.copyWith(color: color, fontWeight: _boldWeight), + small: small.copyWith(color: color), + smallBold: small.copyWith(color: color, fontWeight: _boldWeight), + mini: mini.copyWith(color: color), + miniBold: mini.copyWith(color: color, fontWeight: _boldWeight), + tiny: tiny.copyWith(color: color), + tinyBold: tiny.copyWith(color: color, fontWeight: _boldWeight), + brandSmall: brandStyleSmall.copyWith(color: color), + brandMedium: brandStyleMedium.copyWith(color: color), + h1Muted: h1.copyWith(color: textMuted), + h2Muted: h2.copyWith(color: textMuted), + h3Muted: h3.copyWith(color: textMuted), + largeMuted: large.copyWith(color: textMuted), + bodyMuted: body.copyWith(color: textMuted), + smallMuted: small.copyWith(color: textMuted), + miniMuted: mini.copyWith(color: textMuted), + miniBoldMuted: mini.copyWith(color: textMuted, fontWeight: _boldWeight), + tinyMuted: tiny.copyWith(color: textMuted), + h1Faint: h1.copyWith(color: textFaint), + h2Faint: h2.copyWith(color: textFaint), + h3Faint: h3.copyWith(color: textFaint), + largeFaint: large.copyWith(color: textFaint), + bodyFaint: body.copyWith(color: textFaint), + smallFaint: small.copyWith(color: textFaint), + miniFaint: mini.copyWith(color: textFaint), + tinyFaint: tiny.copyWith(color: textFaint), + ); +} diff --git a/mobile/packages/ui/lib/utils/dialog_util.dart b/mobile/packages/ui/lib/utils/dialog_util.dart new file mode 100644 index 0000000000..6a1d18b5b7 --- /dev/null +++ b/mobile/packages/ui/lib/utils/dialog_util.dart @@ -0,0 +1,373 @@ +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:ente_base/typedefs.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/action_sheet_widget.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_result.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/dialog_widget.dart'; +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/components/progress_dialog.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +typedef DialogBuilder = DialogWidget Function(BuildContext context); + +///Will return null if dismissed by tapping outside +Future showErrorDialog( + BuildContext context, + String title, + String? body, + String primaryButtonLabel, + FutureVoidCallback primaryButtonAction, { + bool isDismissable = true, +}) async { + return showDialogWidget( + context: context, + title: title, + body: body, + isDismissible: isDismissable, + buttons: [ + ButtonWidget( + buttonType: ButtonType.primary, + labelText: primaryButtonLabel, + isInAlert: true, + buttonAction: ButtonAction.first, + onTap: () async { + await primaryButtonAction(); + }, + ), + const ButtonWidget( + buttonType: ButtonType.secondary, + labelText: "OK", + isInAlert: true, + buttonAction: ButtonAction.second, + ), + ], + ); +} + +String parseErrorForUI( + BuildContext context, + String genericError, { + Object? error, + bool surfaceError = kDebugMode, +}) { + try { + if (error == null) { + return genericError; + } + if (error is DioException) { + final DioException dioError = error; + if (dioError.type == DioExceptionType.unknown) { + if (dioError.error.toString().contains('Failed host lookup')) { + return context.strings.networkHostLookUpErr; + } else if (dioError.error.toString().contains('SocketException')) { + return context.strings.networkConnectionRefusedErr; + } + } + } + // return generic error if the user is not internal and the error is not in debug mode + if (!kDebugMode) { + return genericError; + } + String errorInfo = ""; + if (error is DioException) { + final DioException dioError = error; + if (dioError.type == DioExceptionType.badResponse) { + if (dioError.response?.data["code"] != null) { + errorInfo = "Reason: ${dioError.response!.data["code"]}"; + } else { + errorInfo = "Reason: ${dioError.response!.data.toString()}"; + } + } else if (dioError.type == DioExceptionType.badCertificate) { + errorInfo = "Reason: ${dioError.error.toString()}"; + } else { + errorInfo = "Reason: ${dioError.type.toString()}"; + } + } else { + if (kDebugMode) { + errorInfo = error.toString(); + } else { + errorInfo = error.toString().split('Source stack')[0]; + } + } + if (errorInfo.isNotEmpty) { + return "$genericError\n\n$errorInfo"; + } + return genericError; + } catch (e) { + return genericError; + } +} + +///Will return null if dismissed by tapping outside +Future showGenericErrorDialog({ + required BuildContext context, + bool isDismissible = true, + required Object? error, +}) async { + String errorBody = parseErrorForUI( + context, + context.strings.itLooksLikeSomethingWentWrongPleaseRetryAfterSome, + error: error, + ); + bool isWindowCertError = false; + if (Platform.isWindows && + error != null && + error.toString().contains("CERTIFICATE_VERIFY_FAILED")) { + isWindowCertError = true; + errorBody = + "Certificate verification failed. Please update your system certificates, & restart the app. If the issue persists, please contact support."; + } + + return showDialogWidget( + context: context, + title: context.strings.error, + icon: Icons.error_outline_outlined, + body: errorBody, + isDismissible: isDismissible, + buttons: [ + ButtonWidget( + buttonType: ButtonType.primary, + labelText: context.strings.ok, + buttonAction: ButtonAction.first, + isInAlert: true, + ), + if (isWindowCertError) + ButtonWidget( + buttonType: ButtonType.neutral, + labelText: 'Update Certificates', + buttonAction: ButtonAction.third, + isInAlert: true, + onTap: () async { + PlatformUtil.openWebView( + context, + context.strings.faq, + "https://help.ente.io/auth/troubleshooting/windows-login", + ); + }, + ), + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.contactSupport, + buttonAction: ButtonAction.second, + onTap: () async { + await sendLogs( + context, + context.strings.contactSupport, + postShare: () {}, + ); + }, + ), + ], + ); +} + +DialogWidget choiceDialog({ + required String title, + String? body, + required String firstButtonLabel, + String secondButtonLabel = "Cancel", + ButtonType firstButtonType = ButtonType.neutral, + ButtonType secondButtonType = ButtonType.secondary, + ButtonAction firstButtonAction = ButtonAction.first, + ButtonAction secondButtonAction = ButtonAction.cancel, + FutureVoidCallback? firstButtonOnTap, + FutureVoidCallback? secondButtonOnTap, + bool isCritical = false, + IconData? icon, +}) { + final buttons = [ + ButtonWidget( + buttonType: isCritical ? ButtonType.critical : firstButtonType, + labelText: firstButtonLabel, + isInAlert: true, + onTap: firstButtonOnTap, + buttonAction: firstButtonAction, + ), + ButtonWidget( + buttonType: secondButtonType, + labelText: secondButtonLabel, + isInAlert: true, + onTap: secondButtonOnTap, + buttonAction: secondButtonAction, + ), + ]; + + return DialogWidget(title: title, body: body, buttons: buttons, icon: icon); +} + +///Will return null if dismissed by tapping outside +Future showChoiceDialog( + BuildContext context, { + required String title, + String? body, + required String firstButtonLabel, + String? secondButtonLabel = "Cancel", + ButtonType firstButtonType = ButtonType.neutral, + ButtonType secondButtonType = ButtonType.secondary, + ButtonAction firstButtonAction = ButtonAction.first, + ButtonAction secondButtonAction = ButtonAction.cancel, + FutureVoidCallback? firstButtonOnTap, + FutureVoidCallback? secondButtonOnTap, + bool isCritical = false, + IconData? icon, + bool isDismissible = true, +}) async { + final buttons = [ + ButtonWidget( + buttonType: isCritical ? ButtonType.critical : firstButtonType, + labelText: firstButtonLabel, + isInAlert: true, + onTap: firstButtonOnTap, + buttonAction: firstButtonAction, + ), + if (secondButtonLabel != null) + ButtonWidget( + buttonType: secondButtonType, + labelText: secondButtonLabel, + isInAlert: true, + onTap: secondButtonOnTap, + buttonAction: secondButtonAction, + ), + ]; + return showDialogWidget( + context: context, + title: title, + body: body, + buttons: buttons, + icon: icon, + isDismissible: isDismissible, + ); +} + +///Will return null if dismissed by tapping outside +Future showChoiceActionSheet( + BuildContext context, { + required String title, + String? body, + required String firstButtonLabel, + String secondButtonLabel = "Cancel", + ButtonType firstButtonType = ButtonType.neutral, + ButtonType secondButtonType = ButtonType.secondary, + ButtonAction firstButtonAction = ButtonAction.first, + ButtonAction secondButtonAction = ButtonAction.cancel, + FutureVoidCallback? firstButtonOnTap, + FutureVoidCallback? secondButtonOnTap, + bool isCritical = false, + IconData? icon, + bool isDismissible = true, +}) async { + final buttons = [ + ButtonWidget( + buttonType: isCritical ? ButtonType.critical : firstButtonType, + labelText: firstButtonLabel, + isInAlert: true, + onTap: firstButtonOnTap, + buttonAction: firstButtonAction, + shouldStickToDarkTheme: true, + ), + ButtonWidget( + buttonType: secondButtonType, + labelText: secondButtonLabel, + isInAlert: true, + onTap: secondButtonOnTap, + buttonAction: secondButtonAction, + shouldStickToDarkTheme: true, + ), + ]; + return showActionSheet( + context: context, + title: title, + body: body, + buttons: buttons, + isDismissible: isDismissible, + ); +} + +ProgressDialog createProgressDialog( + BuildContext context, + String message, { + isDismissible = false, +}) { + final dialog = ProgressDialog( + context, + type: ProgressDialogType.normal, + isDismissible: isDismissible, + barrierColor: Colors.black12, + ); + dialog.style( + message: message, + messageTextStyle: Theme.of(context).textTheme.labelMedium, + backgroundColor: Theme.of(context).dialogTheme.backgroundColor, + progressWidget: const EnteLoadingWidget(), + borderRadius: 10, + elevation: 10.0, + insetAnimCurve: Curves.easeInOut, + ); + return dialog; +} + +//Can return ButtonResult? from ButtonWidget or Exception? from TextInputDialog +Future showTextInputDialog( + BuildContext context, { + required String title, + String? body, + required String submitButtonLabel, + IconData? icon, + String? label, + String? message, + String? hintText, + required FutureVoidCallbackParamStr onSubmit, + IconData? prefixIcon, + String? initialValue, + Alignment? alignMessage, + int? maxLength, + bool showOnlyLoadingState = false, + TextCapitalization textCapitalization = TextCapitalization.none, + bool alwaysShowSuccessState = false, + bool isPasswordInput = false, + bool useRootNavigator = false, +}) { + return showDialog( + barrierColor: backdropFaintDark, + useRootNavigator: useRootNavigator, + context: context, + builder: (context) { + final bottomInset = MediaQuery.of(context).viewInsets.bottom; + final isKeyboardUp = bottomInset > 100; + return Material( + color: Colors.transparent, + child: Center( + child: Padding( + padding: EdgeInsets.only(bottom: isKeyboardUp ? bottomInset : 0), + child: TextInputDialog( + title: title, + message: message, + label: label, + body: body, + icon: icon, + submitButtonLabel: submitButtonLabel, + onSubmit: onSubmit, + hintText: hintText, + prefixIcon: prefixIcon, + initialValue: initialValue, + alignMessage: alignMessage, + maxLength: maxLength, + showOnlyLoadingState: showOnlyLoadingState, + textCapitalization: textCapitalization, + alwaysShowSuccessState: alwaysShowSuccessState, + isPasswordInput: isPasswordInput, + ), + ), + ), + ); + }, + ); +} diff --git a/mobile/packages/ui/lib/utils/file_icon_utils.dart b/mobile/packages/ui/lib/utils/file_icon_utils.dart new file mode 100644 index 0000000000..bc1f238200 --- /dev/null +++ b/mobile/packages/ui/lib/utils/file_icon_utils.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; + +class FileIconConfig { + final IconData icon; + final Color color; + final Set extensions; + + const FileIconConfig({ + required this.icon, + required this.color, + required this.extensions, + }); +} + +class FileIconUtils { + // Centralized configuration - change icons and colors here only + static const Map _fileTypeConfigs = { + 'pdf': FileIconConfig( + extensions: {'.pdf'}, + icon: Icons.picture_as_pdf, + color: Colors.red, + ), + 'image': FileIconConfig( + extensions: {'.jpg', '.png', '.heic'}, + icon: Icons.image, + color: Colors.blue, + ), + 'presentation': FileIconConfig( + extensions: {'.pptx'}, + icon: Icons.slideshow, + color: Colors.orange, + ), + 'spreadsheet': FileIconConfig( + extensions: {'.xlsx'}, + icon: Icons.table_chart, + color: Colors.green, + ), + }; + + static const FileIconConfig _defaultConfig = FileIconConfig( + extensions: {}, + icon: Icons.insert_drive_file, + color: Colors.grey, + ); + + static FileIconConfig _getFileConfig(String fileName) { + final lowerFileName = fileName.toLowerCase(); + final lastDotIndex = lowerFileName.lastIndexOf('.'); + + if (lastDotIndex == -1) { + return _defaultConfig; // No extension found + } + + final extension = lowerFileName.substring(lastDotIndex); + + for (final config in _fileTypeConfigs.values) { + if (config.extensions.contains(extension)) { + return config; + } + } + + return _defaultConfig; + } + + static IconData getFileIcon(String fileName) { + return _getFileConfig(fileName).icon; + } + + static Color getFileIconColor(String fileName) { + return _getFileConfig(fileName).color; + } +} diff --git a/mobile/packages/ui/lib/utils/toast_util.dart b/mobile/packages/ui/lib/utils/toast_util.dart new file mode 100644 index 0000000000..ce3693ef87 --- /dev/null +++ b/mobile/packages/ui/lib/utils/toast_util.dart @@ -0,0 +1,52 @@ +import '../theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +void showToast( + BuildContext context, + String message, { + toastLength = Toast.LENGTH_LONG, + iOSDismissOnTap = true, +}) async { + try { + await Fluttertoast.cancel(); + await Fluttertoast.showToast( + msg: message, + toastLength: toastLength, + gravity: ToastGravity.BOTTOM, + timeInSecForIosWeb: 1, + backgroundColor: getEnteColorScheme(context).toastBackgroundColor, + textColor: getEnteColorScheme(context).toastTextColor, + fontSize: 16.0, + ); + } on MissingPluginException catch (_) { + Widget toast = Container( + padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(25.0), + color: getEnteColorScheme(context).toastBackgroundColor, + ), + child: Text( + message, + style: TextStyle( + color: getEnteColorScheme(context).toastTextColor, + fontSize: 16.0, + ), + ), + ); + + final fToast = FToast(); + fToast.init(context); + + fToast.showToast( + child: toast, + gravity: ToastGravity.BOTTOM, + toastDuration: const Duration(seconds: 2), + ); + } +} + +void showShortToast(context, String message) { + showToast(context, message, toastLength: Toast.LENGTH_SHORT); +} diff --git a/mobile/packages/ui/lib/utils/window_listener_service.dart b/mobile/packages/ui/lib/utils/window_listener_service.dart new file mode 100644 index 0000000000..0a1f391c50 --- /dev/null +++ b/mobile/packages/ui/lib/utils/window_listener_service.dart @@ -0,0 +1,41 @@ +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/widgets.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:window_manager/window_manager.dart'; + +class WindowListenerService { + static const double initialWindowHeight = 1200.0; + static const double initialWindowWidth = 800.0; + static const double maxWindowHeight = 8192.0; + static const double maxWindowWidth = 8192.0; + late SharedPreferences _preferences; + + WindowListenerService._privateConstructor(); + + static final WindowListenerService instance = + WindowListenerService._privateConstructor(); + + Future init() async { + _preferences = await SharedPreferences.getInstance(); + } + + Size getWindowSize() { + final double windowWidth = + _preferences.getDouble('windowWidth') ?? initialWindowWidth; + final double windowHeight = + _preferences.getDouble('windowHeight') ?? initialWindowHeight; + final w = windowWidth.clamp(200.0, maxWindowWidth); + final h = windowHeight.clamp(400.0, maxWindowHeight); + return Size(w, h); + } + + Future onWindowResize() async { + final width = (await windowManager.getSize()).width; + final height = (await windowManager.getSize()).height; + // Save the window size to shared preferences + await _preferences.setDouble('windowWidth', width); + await _preferences.setDouble('windowHeight', height); + } +} diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock new file mode 100644 index 0000000000..a25bb42857 --- /dev/null +++ b/mobile/packages/ui/pubspec.lock @@ -0,0 +1,1002 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: transitive + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + email_validator: + dependency: transitive + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_base: + dependency: "direct main" + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: transitive + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: transitive + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: transitive + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: "direct main" + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../strings" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: "direct main" + description: + path: "../utils" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_saver: + dependency: transitive + description: + name: file_saver + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" + url: "https://pub.dev" + source: hosted + version: "0.2.14" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_email_sender: + dependency: transitive + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: "direct main" + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: "direct main" + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: "direct main" + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: transitive + description: + name: share_plus + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.dev" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.dev" + source: hosted + version: "5.0.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml new file mode 100644 index 0000000000..e501e11fad --- /dev/null +++ b/mobile/packages/ui/pubspec.yaml @@ -0,0 +1,35 @@ +name: ente_ui +description: A Flutter package containing shared UI components, themes, and utilities for Ente apps +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + dio: ^5.8.0+1 + ente_base: + path: ../../packages/base + ente_configuration: + path: ../../packages/configuration + ente_logging: + path: ../../packages/logging + ente_strings: + path: ../../packages/strings + ente_utils: + path: ../../packages/utils + flutter_inappwebview: ^6.1.5 + fluttertoast: ^8.1.1 + modal_bottom_sheet: ^3.0.0 + shared_preferences: ^2.5.3 + window_manager: ^0.5.0 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From f8aff3e12b2983465b4ed4cfe42a49d1eb587bc0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 21:07:00 +0530 Subject: [PATCH 023/164] Update common strings --- .../strings/lib/l10n/arb/strings_en.arb | 27 +++++++++++++++++- .../lib/l10n/strings_localizations.dart | 28 +++++++++++++++++-- .../lib/l10n/strings_localizations_ar.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_bg.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_cs.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_da.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_el.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_en.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_es.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_fr.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_id.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_ja.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_ko.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_lt.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_nl.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_pl.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_pt.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_ru.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_sk.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_sr.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_sv.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_tr.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_vi.dart | 16 ++++++++++- .../lib/l10n/strings_localizations_zh.dart | 16 ++++++++++- 24 files changed, 382 insertions(+), 25 deletions(-) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index a7175cb422..c8054103af 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -119,5 +119,30 @@ "@viewLogs": { "description": "Button to view logs" }, - "customEndpoint": "Connected to {endpoint}" + "customEndpoint": "Connected to {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Save", + "@save": { + "description": "Label for save button" + }, + "send": "Send", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Do you want to save this to your storage (Downloads folder by default) or send it to other apps?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Do you want to save this to your storage (Downloads folder by default)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index ab02835b86..2b565509c9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -302,11 +302,35 @@ abstract class StringsLocalizations { /// **'View logs'** String get viewLogs; - /// No description provided for @customEndpoint. + /// Text showing user is connected to a custom endpoint /// /// In en, this message translates to: /// **'Connected to {endpoint}'** - String customEndpoint(Object endpoint); + String customEndpoint(String endpoint); + + /// Label for save button + /// + /// In en, this message translates to: + /// **'Save'** + String get save; + + /// Label for send button + /// + /// In en, this message translates to: + /// **'Send'** + String get send; + + /// Description text asking user if they want to save to storage or share with other apps + /// + /// In en, this message translates to: + /// **'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'** + String get saveOrSendDescription; + + /// Description text asking user if they want to save to storage (for platforms that don't support sharing) + /// + /// In en, this message translates to: + /// **'Do you want to save this to your storage (Downloads folder by default)?'** + String get saveOnlyDescription; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index fccdc52045..b827fd5378 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -99,7 +99,21 @@ class StringsLocalizationsAr extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 92d03e2e38..304e256092 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -99,7 +99,21 @@ class StringsLocalizationsBg extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index c641b12607..686cafd26a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -99,7 +99,21 @@ class StringsLocalizationsCs extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index de3b14b5b6..f6aa408fef 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -99,7 +99,21 @@ class StringsLocalizationsDa extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index de3d222597..0602fe735b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -99,7 +99,21 @@ class StringsLocalizationsEl extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index ab264897c9..430cdaf61d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -99,7 +99,21 @@ class StringsLocalizationsEn extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 9ffde6ed0d..14f48212c4 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -99,7 +99,21 @@ class StringsLocalizationsEs extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index ab7d35b1b8..68f53416f5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -99,7 +99,21 @@ class StringsLocalizationsFr extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index ce8f373a04..24d31ecf93 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -99,7 +99,21 @@ class StringsLocalizationsId extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 69f0c19d40..9a69707f86 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -99,7 +99,21 @@ class StringsLocalizationsJa extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index c572a95eae..afff907060 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -99,7 +99,21 @@ class StringsLocalizationsKo extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 4353c016d8..06e937630a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -99,7 +99,21 @@ class StringsLocalizationsLt extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 276a6d4866..d909e38fe7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -99,7 +99,21 @@ class StringsLocalizationsNl extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index c8ffbcd073..2f54ceb1a7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -99,7 +99,21 @@ class StringsLocalizationsPl extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index 7190ab436c..ccd842665f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -99,7 +99,21 @@ class StringsLocalizationsPt extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index c5345f29dd..47a47041d8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -99,7 +99,21 @@ class StringsLocalizationsRu extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 30934edbc7..7aab379f90 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -99,7 +99,21 @@ class StringsLocalizationsSk extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index f06875959f..22221537bd 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -99,7 +99,21 @@ class StringsLocalizationsSr extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 0e69b88706..ca90e38c6f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -99,7 +99,21 @@ class StringsLocalizationsSv extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index ae94a5d61e..c4fdd62e1e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -99,7 +99,21 @@ class StringsLocalizationsTr extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 68048de3fb..906a5a1360 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -99,7 +99,21 @@ class StringsLocalizationsVi extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 41dd769a4b..af1619db26 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -98,9 +98,23 @@ class StringsLocalizationsZh extends StringsLocalizations { String get viewLogs => 'View logs'; @override - String customEndpoint(Object endpoint) { + String customEndpoint(String endpoint) { return 'Connected to $endpoint'; } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From d5c1970ca223acbc062468178811d2c907be90f0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 21:07:09 +0530 Subject: [PATCH 024/164] Update common ui --- mobile/packages/ui/lib/utils/dialog_util.dart | 8 +++----- mobile/packages/ui/lib/utils/toast_util.dart | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/mobile/packages/ui/lib/utils/dialog_util.dart b/mobile/packages/ui/lib/utils/dialog_util.dart index 6a1d18b5b7..620f4e0cb7 100644 --- a/mobile/packages/ui/lib/utils/dialog_util.dart +++ b/mobile/packages/ui/lib/utils/dialog_util.dart @@ -22,9 +22,7 @@ typedef DialogBuilder = DialogWidget Function(BuildContext context); Future showErrorDialog( BuildContext context, String title, - String? body, - String primaryButtonLabel, - FutureVoidCallback primaryButtonAction, { + String? body, { bool isDismissable = true, }) async { return showDialogWidget( @@ -35,11 +33,11 @@ Future showErrorDialog( buttons: [ ButtonWidget( buttonType: ButtonType.primary, - labelText: primaryButtonLabel, + labelText: context.strings.contactSupport, isInAlert: true, buttonAction: ButtonAction.first, onTap: () async { - await primaryButtonAction(); + await openSupportPage(body, null); }, ), const ButtonWidget( diff --git a/mobile/packages/ui/lib/utils/toast_util.dart b/mobile/packages/ui/lib/utils/toast_util.dart index ce3693ef87..8b03a1f778 100644 --- a/mobile/packages/ui/lib/utils/toast_util.dart +++ b/mobile/packages/ui/lib/utils/toast_util.dart @@ -1,4 +1,4 @@ -import '../theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:fluttertoast/fluttertoast.dart'; From 8a9f73ada5ea2450344a2cad1feaa39797aafac7 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Sat, 19 Jul 2025 21:07:16 +0530 Subject: [PATCH 025/164] Refactor utils --- mobile/packages/utils/lib/share_utils.dart | 50 ++++++++++++++++++++++ mobile/packages/utils/pubspec.lock | 8 ++++ 2 files changed, 58 insertions(+) diff --git a/mobile/packages/utils/lib/share_utils.dart b/mobile/packages/utils/lib/share_utils.dart index c7cf0e8de5..884901c442 100644 --- a/mobile/packages/utils/lib/share_utils.dart +++ b/mobile/packages/utils/lib/share_utils.dart @@ -1,7 +1,57 @@ +import 'dart:io'; + +import 'package:ente_strings/extensions.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/dialog_widget.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:share_plus/share_plus.dart'; +Future shareDialog( + BuildContext context, + String title, { + required Function saveAction, + required Function sendAction, +}) async { + final l10n = context.strings; + await showDialogWidget( + context: context, + title: title, + body: Platform.isLinux || Platform.isWindows + ? l10n.saveOnlyDescription + : l10n.saveOrSendDescription, + buttons: [ + ButtonWidget( + isInAlert: true, + buttonType: ButtonType.neutral, + labelText: l10n.save, + buttonAction: ButtonAction.first, + shouldSurfaceExecutionStates: false, + onTap: () async { + await saveAction(); + }, + ), + if (!Platform.isWindows && !Platform.isLinux) + ButtonWidget( + isInAlert: true, + buttonType: ButtonType.secondary, + labelText: l10n.send, + buttonAction: ButtonAction.second, + onTap: () async { + await sendAction(); + }, + ), + ButtonWidget( + isInAlert: true, + buttonType: ButtonType.secondary, + labelText: l10n.cancel, + buttonAction: ButtonAction.cancel, + ), + ], + ); +} + Rect _sharePosOrigin(BuildContext? context, GlobalKey? key) { late final Rect rect; if (context != null) { diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index 9b33654cf8..ecc483f51a 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -528,6 +528,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.0" + modal_bottom_sheet: + dependency: transitive + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" package_info_plus: dependency: "direct main" description: From f9299e79502e541f3f4f35a2a77a6d2e6129324c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 12:38:01 +0530 Subject: [PATCH 026/164] Reduce noise --- mobile/packages/configuration/README.md | 21 ------ mobile/packages/network/README.md | 37 ---------- mobile/packages/strings/README.md | 95 ------------------------- 3 files changed, 153 deletions(-) delete mode 100644 mobile/packages/configuration/README.md delete mode 100644 mobile/packages/network/README.md delete mode 100644 mobile/packages/strings/README.md diff --git a/mobile/packages/configuration/README.md b/mobile/packages/configuration/README.md deleted file mode 100644 index 59d435360f..0000000000 --- a/mobile/packages/configuration/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Configuration - -A Flutter package for shared configuration across ente apps. - -## Usage - -Import the package and call the init method from your app's main.dart: - -```dart -import 'package:configuration/configuration.dart'; - -void main() async { - await Configuration.init(); - // ... rest of your app initialization -} -``` - -## Features - -- Shared configuration initialization -- Common setup logic for ente apps diff --git a/mobile/packages/network/README.md b/mobile/packages/network/README.md deleted file mode 100644 index 9311f94cc8..0000000000 --- a/mobile/packages/network/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# Ente Network - -A Flutter package for network management and HTTP client configuration used across Ente applications. - -## Features - -- Configurable HTTP client using Dio -- Request interceptors for authentication and request tracking -- Platform-aware user agent handling -- Connection timeout management -- Base URL configuration -- Request ID generation - -## Usage - -```dart -import 'package:ente_network/network.dart'; -import 'package:ente_configuration/base_configuration.dart'; - -// Initialize the network service -await Network.instance.init(configuration); - -// Use the configured Dio instances -final dio = Network.instance.getDio(); -final enteDio = Network.instance.enteDio; -``` - -## Dependencies - -This package depends on: -- `dio` for HTTP client functionality -- `ente_configuration` for configuration management -- `ente_events` for event handling -- `native_dio_adapter` for native networking -- `package_info_plus` for package information -- `ua_client_hints` for user agent generation -- `uuid` for request ID generation diff --git a/mobile/packages/strings/README.md b/mobile/packages/strings/README.md deleted file mode 100644 index 9830f72753..0000000000 --- a/mobile/packages/strings/README.md +++ /dev/null @@ -1,95 +0,0 @@ -# Ente Strings - -A Flutter package containing shared localization strings for Ente apps. - -## Purpose - -This package provides common localization strings that are shared across multiple Ente applications (Auth, Photos, etc.). It centralizes the translations for common UI elements, error messages, and other shared text to ensure consistency across the apps. - -## Usage - -### 1. Add to pubspec.yaml - -```yaml -dependencies: - ente_strings: - path: ../packages/strings -``` - -### 2. Configure in your app - -Add the strings localizations delegate to your app: - -```dart -import 'package:ente_strings/ente_strings.dart'; - -MaterialApp( - localizationsDelegates: [ - ...StringsLocalizations.localizationsDelegates, - // Your other delegates... - ], - supportedLocales: StringsLocalizations.supportedLocales, - // ... -) -``` - -### 3. Use in your widgets - -Use the convenient extension to access strings: - -```dart -import 'package:ente_strings/ente_strings.dart'; - -Widget build(BuildContext context) { - return Text(context.strings.networkHostLookUpErr); -} -``` - -Or use the traditional approach: - -```dart -import 'package:ente_strings/ente_strings.dart'; - -Widget build(BuildContext context) { - return Text(StringsLocalizations.of(context).networkHostLookUpErr); -} -``` - -## Available Strings - -Currently available strings: - -- `networkHostLookUpErr`: Error message for network host lookup failures - -## Adding New Strings - -1. Add the string to `lib/l10n/arb/strings_en.arb` (template file) -2. Add translations to all other `strings_*.arb` files -3. Run `flutter gen-l10n` to regenerate the localization files -4. Move generated files from `lib/l10n/arb/` to `lib/l10n/` if needed - -## Supported Languages - -Currently supported languages include: -- Arabic (ar) -- Bulgarian (bg) -- Czech (cs) -- Danish (da) -- Greek (el) -- English (en) -- French (fr) -- Indonesian (id) -- Japanese (ja) -- Korean (ko) -- Lithuanian (lt) -- Dutch (nl) -- Polish (pl) -- Portuguese (pt) -- Russian (ru) -- Slovak (sk) -- Serbian (sr) -- Swedish (sv) -- Turkish (tr) -- Vietnamese (vi) -- Chinese Simplified (zh) -- Chinese Traditional (zh_TW) From b444bdc5ecd376ad222bbfb58dfecb1899625351 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 12:41:36 +0530 Subject: [PATCH 027/164] Add re-usable base home page --- mobile/packages/ui/lib/pages/base_home_page.dart | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 mobile/packages/ui/lib/pages/base_home_page.dart diff --git a/mobile/packages/ui/lib/pages/base_home_page.dart b/mobile/packages/ui/lib/pages/base_home_page.dart new file mode 100644 index 0000000000..73f08c3db2 --- /dev/null +++ b/mobile/packages/ui/lib/pages/base_home_page.dart @@ -0,0 +1,7 @@ +import 'package:flutter/material.dart'; + +abstract class BaseHomePage extends StatefulWidget { + const BaseHomePage({super.key}); +} + +abstract class BaseHomePageState extends State {} From a7a162d37505a1067c1ecc818f106544e134acc3 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 13:58:26 +0530 Subject: [PATCH 028/164] Update utils --- mobile/packages/utils/lib/date_time_util.dart | 217 ++++++++++++++++++ mobile/packages/utils/lib/platform_util.dart | 4 +- mobile/packages/utils/pubspec.lock | 12 +- mobile/packages/utils/pubspec.yaml | 4 +- 4 files changed, 227 insertions(+), 10 deletions(-) create mode 100644 mobile/packages/utils/lib/date_time_util.dart diff --git a/mobile/packages/utils/lib/date_time_util.dart b/mobile/packages/utils/lib/date_time_util.dart new file mode 100644 index 0000000000..0d566904a4 --- /dev/null +++ b/mobile/packages/utils/lib/date_time_util.dart @@ -0,0 +1,217 @@ +import 'package:intl/intl.dart'; + +const Set monthWith31Days = {1, 3, 5, 7, 8, 10, 12}; +const Set monthWith30Days = {4, 6, 9, 11}; +Map _months = { + 1: "Jan", + 2: "Feb", + 3: "March", + 4: "April", + 5: "May", + 6: "Jun", + 7: "July", + 8: "Aug", + 9: "Sep", + 10: "Oct", + 11: "Nov", + 12: "Dec", +}; + +Map _fullMonths = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December", +}; + +Map _days = { + 1: "Mon", + 2: "Tue", + 3: "Wed", + 4: "Thu", + 5: "Fri", + 6: "Sat", + 7: "Sun", +}; + +final currentYear = int.parse(DateTime.now().year.toString()); +const searchStartYear = 1970; + +//Jun 2022 +String getMonthAndYear(DateTime dateTime) { + return "${_months[dateTime.month]!} ${dateTime.year}"; +} + +//Thu, 30 Jun +String getDayAndMonth(DateTime dateTime) { + return "${_days[dateTime.weekday]!}, ${dateTime.day} ${_months[dateTime.month]!}"; +} + +//30 Jun, 2022 +String getDateAndMonthAndYear(DateTime dateTime) { + return "${dateTime.day} ${_months[dateTime.month]!}, ${dateTime.year}"; +} + +String getDay(DateTime dateTime) { + return _days[dateTime.weekday]!; +} + +String getMonth(DateTime dateTime) { + return _months[dateTime.month]!; +} + +String getFullMonth(DateTime dateTime) { + return _fullMonths[dateTime.month]!; +} + +String getAbbreviationOfYear(DateTime dateTime) { + return (dateTime.year % 100).toString(); +} + +//14:32 +String getTime(DateTime dateTime) { + final hours = + dateTime.hour > 9 ? dateTime.hour.toString() : "0${dateTime.hour}"; + final minutes = + dateTime.minute > 9 ? dateTime.minute.toString() : "0${dateTime.minute}"; + return "$hours:$minutes"; +} + +//11:22 AM +String getTimeIn12hrFormat(DateTime dateTime) { + return DateFormat.jm().format(dateTime); +} + +//Thu, Jun 30, 2022 - 14:32 +String getFormattedTime(DateTime dateTime) { + return "${getDay(dateTime)}, ${getMonth(dateTime)} ${dateTime.day}, ${dateTime.year} - ${getTime(dateTime)}"; +} + +//30 Jun'22 +String getFormattedDate(DateTime dateTime) { + return "${dateTime.day} ${getMonth(dateTime)}'${getAbbreviationOfYear(dateTime)}"; +} + +String getFullDate(DateTime dateTime) { + return "${getDay(dateTime)}, ${getMonth(dateTime)} ${dateTime.day} ${dateTime.year}"; +} + +String daysLeft(int futureTime) { + final int daysLeft = ((futureTime - DateTime.now().microsecondsSinceEpoch) / + Duration.microsecondsPerDay) + .ceil(); + return '$daysLeft day${daysLeft <= 1 ? "" : "s"}'; +} + +String formatDuration(Duration position) { + final ms = position.inMilliseconds; + + int seconds = ms ~/ 1000; + final int hours = seconds ~/ 3600; + seconds = seconds % 3600; + final minutes = seconds ~/ 60; + seconds = seconds % 60; + + final hoursString = hours >= 10 + ? '$hours' + : hours == 0 + ? '00' + : '0$hours'; + + final minutesString = minutes >= 10 + ? '$minutes' + : minutes == 0 + ? '00' + : '0$minutes'; + + final secondsString = seconds >= 10 + ? '$seconds' + : seconds == 0 + ? '00' + : '0$seconds'; + + final formattedTime = + '${hoursString == '00' ? '' : '$hoursString:'}$minutesString:$secondsString'; + + return formattedTime; +} + +bool isLeapYear(DateTime dateTime) { + final year = dateTime.year; + if (year % 4 == 0) { + if (year % 100 == 0) { + if (year % 400 == 0) { + return true; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } +} + +String getDayTitle(int timestamp) { + final date = DateTime.fromMicrosecondsSinceEpoch(timestamp); + final now = DateTime.now(); + var title = getDayAndMonth(date); + if (date.year == now.year && date.month == now.month) { + if (date.day == now.day) { + title = "Today"; + } else if (date.day == now.day - 1) { + title = "Yesterday"; + } + } + if (date.year != DateTime.now().year) { + title += " ${date.year}"; + } + return title; +} + +String secondsToHHMMSS(int value) { + int h, m, s; + h = value ~/ 3600; + m = ((value - h * 3600)) ~/ 60; + s = value - (h * 3600) - (m * 60); + final String hourLeft = h.toString().length < 2 ? "0$h" : h.toString(); + + final String minuteLeft = m.toString().length < 2 ? "0$m" : m.toString(); + + final String secondsLeft = s.toString().length < 2 ? "0$s" : s.toString(); + + final String result = "$hourLeft:$minuteLeft:$secondsLeft"; + + return result; +} + +bool isValidDate({ + required int day, + required int month, + required int year, +}) { + if (day < 0 || day > 31 || month < 0 || month > 12 || year < 0) { + return false; + } + if (monthWith30Days.contains(month) && day > 30) { + return false; + } + if (month == 2) { + if (day > 29) { + return false; + } + if (day == 29 && year % 4 != 0) { + return false; + } + } + return true; +} diff --git a/mobile/packages/utils/lib/platform_util.dart b/mobile/packages/utils/lib/platform_util.dart index e84349c301..6ad91b0d88 100644 --- a/mobile/packages/utils/lib/platform_util.dart +++ b/mobile/packages/utils/lib/platform_util.dart @@ -43,14 +43,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index ecc483f51a..982e79b216 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -224,10 +224,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.2.14" + version: "0.3.1" fixnum: dependency: transitive description: @@ -700,18 +700,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 url: "https://pub.dev" source: hosted - version: "10.1.4" + version: "11.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" shared_preferences: dependency: transitive description: diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 648b0f0ac6..3a6a77e4ea 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: # Core dependencies archive: ^4.0.7 email_validator: ^3.0.0 - file_saver: ^0.2.14 + file_saver: ^0.3.0 flutter_email_sender: ^7.0.0 ente_logging: path: ../../packages/logging @@ -26,7 +26,7 @@ dependencies: package_info_plus: ^8.1.1 path: ^1.9.1 path_provider: ^2.1.5 - share_plus: ^10.1.2 + share_plus: ^11.0.0 url_launcher: ^6.3.1 window_manager: ^0.5.0 From a412aa488697d2892b7ebf1878ef4a67e9d905cb Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:38:15 +0530 Subject: [PATCH 029/164] Update strings --- .../strings/lib/l10n/arb/strings_en.arb | 411 ++++++++++++ .../lib/l10n/strings_localizations.dart | 600 ++++++++++++++++++ .../lib/l10n/strings_localizations_ar.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_bg.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_cs.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_da.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_el.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_en.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_es.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_fr.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_id.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_ja.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_ko.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_lt.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_nl.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_pl.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_pt.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_ru.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_sk.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_sr.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_sv.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_tr.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_vi.dart | 328 ++++++++++ .../lib/l10n/strings_localizations_zh.dart | 328 ++++++++++ 24 files changed, 8227 insertions(+) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index c8054103af..9f5b231302 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -144,5 +144,416 @@ "saveOnlyDescription": "Do you want to save this to your storage (Downloads folder by default)?", "@saveOnlyDescription": { "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Enter your new email address", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Verify", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Invalid email address", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Please enter a valid email address.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Please wait...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verify password", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Incorrect password", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Please try again", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Enter password", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Enter your password", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Active sessions", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Oops", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Something went wrong, please try again", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "This will log you out of this device!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "This will log you out of the following device:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Terminate session?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Terminate", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "This device", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Create account", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Weak", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Moderate", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Strong", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Delete account", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "We'll be sorry to see you go. Are you facing some issue?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Yes, send feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "No, delete account", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Please authenticate to initiate account deletion", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirm account deletion", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Delete", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Create new account", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Password", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirm password", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Password strength: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "How did you hear about Ente? (optional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "We don't track app installs. It'd help if you told us where you found us!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "I agree to the terms of service and privacy policy", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Terms", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privacy Policy", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Encryption", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Log in", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Welcome back!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "By clicking log in, I agree to the terms of service and privacy policy", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "No internet connection", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verification failed, please try again", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Recreate password", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "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).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Use recovery key", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Forgot password", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Change email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verify email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "We have sent a mail to {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "To reset your password, please verify your email first.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Please check your inbox (and spam) to complete verification", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tap to enter code", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Send email", + "resendEmail": "Resend email", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Verification is still pending", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Session expired", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Your session has expired. Please login again.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Passkey verification", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Waiting for verification...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Try again", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Check status", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Login with TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Recover account", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Set password", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Change password", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Reset password", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Encryption keys", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Enter a password we can use to encrypt your data", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Enter a new password we can use to encrypt your data", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "We don't store this password, so if you forget, we cannot decrypt your data", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "How it works", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generating encryption keys...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Password changed successfully", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Sign out from other devices", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "If you think someone might know your password, you can force all other devices using your account to sign out.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Sign out other devices", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Do not sign out", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generating encryption keys...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continue", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Insecure device", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Recovery key copied to clipboard", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Recovery key", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "If you forget your password, the only way you can recover your data is with this key.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "We don't store this key, please save this 24 word key in a safe place.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Do this later", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Save key", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Recovery key saved in Downloads folder!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "No recovery key?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Two-factor authentication", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Enter the 6-digit code from\nyour authenticator app", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Lost device?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Enter your recovery key", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recover", + "@recover": { + "description": "Recover button label" } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index 2b565509c9..afe8b80669 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -331,6 +331,606 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'Do you want to save this to your storage (Downloads folder by default)?'** String get saveOnlyDescription; + + /// Hint text for entering new email address + /// + /// In en, this message translates to: + /// **'Enter your new email address'** + String get enterNewEmailHint; + + /// Email field label + /// + /// In en, this message translates to: + /// **'Email'** + String get email; + + /// Verify button label + /// + /// In en, this message translates to: + /// **'Verify'** + String get verify; + + /// Title for invalid email error dialog + /// + /// In en, this message translates to: + /// **'Invalid email address'** + String get invalidEmailTitle; + + /// Message for invalid email error dialog + /// + /// In en, this message translates to: + /// **'Please enter a valid email address.'** + String get invalidEmailMessage; + + /// Please wait message + /// + /// In en, this message translates to: + /// **'Please wait...'** + String get pleaseWait; + + /// Verify password button label + /// + /// In en, this message translates to: + /// **'Verify password'** + String get verifyPassword; + + /// Title for incorrect password error + /// + /// In en, this message translates to: + /// **'Incorrect password'** + String get incorrectPasswordTitle; + + /// Message asking user to try again + /// + /// In en, this message translates to: + /// **'Please try again'** + String get pleaseTryAgain; + + /// Enter password field label + /// + /// In en, this message translates to: + /// **'Enter password'** + String get enterPassword; + + /// Hint for password field + /// + /// In en, this message translates to: + /// **'Enter your password'** + String get enterYourPasswordHint; + + /// Title for active sessions page + /// + /// In en, this message translates to: + /// **'Active sessions'** + String get activeSessions; + + /// Oops error title + /// + /// In en, this message translates to: + /// **'Oops'** + String get oops; + + /// Generic error message + /// + /// In en, this message translates to: + /// **'Something went wrong, please try again'** + String get somethingWentWrongPleaseTryAgain; + + /// Warning message for logging out of current device + /// + /// In en, this message translates to: + /// **'This will log you out of this device!'** + String get thisWillLogYouOutOfThisDevice; + + /// Warning message for logging out of another device + /// + /// In en, this message translates to: + /// **'This will log you out of the following device:'** + String get thisWillLogYouOutOfTheFollowingDevice; + + /// Title for terminate session dialog + /// + /// In en, this message translates to: + /// **'Terminate session?'** + String get terminateSession; + + /// Terminate button label + /// + /// In en, this message translates to: + /// **'Terminate'** + String get terminate; + + /// Label for current device + /// + /// In en, this message translates to: + /// **'This device'** + String get thisDevice; + + /// Create account button label + /// + /// In en, this message translates to: + /// **'Create account'** + String get createAccount; + + /// Weak password strength label + /// + /// In en, this message translates to: + /// **'Weak'** + String get weakStrength; + + /// Moderate password strength label + /// + /// In en, this message translates to: + /// **'Moderate'** + String get moderateStrength; + + /// Strong password strength label + /// + /// In en, this message translates to: + /// **'Strong'** + String get strongStrength; + + /// Delete account button label + /// + /// In en, this message translates to: + /// **'Delete account'** + String get deleteAccount; + + /// Message asking user why they want to delete account + /// + /// In en, this message translates to: + /// **'We\'ll be sorry to see you go. Are you facing some issue?'** + String get deleteAccountQuery; + + /// Button to confirm sending feedback + /// + /// In en, this message translates to: + /// **'Yes, send feedback'** + String get yesSendFeedbackAction; + + /// Button to proceed with account deletion + /// + /// In en, this message translates to: + /// **'No, delete account'** + String get noDeleteAccountAction; + + /// Title for authentication dialog before account deletion + /// + /// In en, this message translates to: + /// **'Please authenticate to initiate account deletion'** + String get initiateAccountDeleteTitle; + + /// Title for account deletion confirmation dialog + /// + /// In en, this message translates to: + /// **'Confirm account deletion'** + String get confirmAccountDeleteTitle; + + /// Message warning about account deletion consequences + /// + /// In en, this message translates to: + /// **'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'** + String get confirmAccountDeleteMessage; + + /// Delete button label + /// + /// In en, this message translates to: + /// **'Delete'** + String get delete; + + /// Create new account button label + /// + /// In en, this message translates to: + /// **'Create new account'** + String get createNewAccount; + + /// Password field label + /// + /// In en, this message translates to: + /// **'Password'** + String get password; + + /// Confirm password field label + /// + /// In en, this message translates to: + /// **'Confirm password'** + String get confirmPassword; + + /// Text to indicate the password strength + /// + /// In en, this message translates to: + /// **'Password strength: {passwordStrengthValue}'** + String passwordStrength(String passwordStrengthValue); + + /// Title asking how user heard about Ente + /// + /// In en, this message translates to: + /// **'How did you hear about Ente? (optional)'** + String get hearUsWhereTitle; + + /// Explanation for asking how user heard about Ente + /// + /// In en, this message translates to: + /// **'We don\'t track app installs. It\'d help if you told us where you found us!'** + String get hearUsExplanation; + + /// Terms agreement text for sign up + /// + /// In en, this message translates to: + /// **'I agree to the terms of service and privacy policy'** + String get signUpTerms; + + /// Terms of service title + /// + /// In en, this message translates to: + /// **'Terms'** + String get termsOfServicesTitle; + + /// Privacy policy title + /// + /// In en, this message translates to: + /// **'Privacy Policy'** + String get privacyPolicyTitle; + + /// Warning about password loss + /// + /// In en, this message translates to: + /// **'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'** + String get ackPasswordLostWarning; + + /// Encryption label + /// + /// In en, this message translates to: + /// **'Encryption'** + String get encryption; + + /// Log in button label + /// + /// In en, this message translates to: + /// **'Log in'** + String get logInLabel; + + /// Welcome back message + /// + /// In en, this message translates to: + /// **'Welcome back!'** + String get welcomeBack; + + /// Terms agreement text for login + /// + /// In en, this message translates to: + /// **'By clicking log in, I agree to the terms of service and privacy policy'** + String get loginTerms; + + /// No internet connection error message + /// + /// In en, this message translates to: + /// **'No internet connection'** + String get noInternetConnection; + + /// Message asking user to check internet connection + /// + /// In en, this message translates to: + /// **'Please check your internet connection and try again.'** + String get pleaseCheckYourInternetConnectionAndTryAgain; + + /// Verification failed error message + /// + /// In en, this message translates to: + /// **'Verification failed, please try again'** + String get verificationFailedPleaseTryAgain; + + /// Title for recreate password dialog + /// + /// In en, this message translates to: + /// **'Recreate password'** + String get recreatePasswordTitle; + + /// Body text for recreate password dialog + /// + /// In en, this message translates to: + /// **'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; + + /// Use recovery key button label + /// + /// In en, this message translates to: + /// **'Use recovery key'** + String get useRecoveryKey; + + /// Forgot password link label + /// + /// In en, this message translates to: + /// **'Forgot password'** + String get forgotPassword; + + /// Change email button label + /// + /// In en, this message translates to: + /// **'Change email'** + String get changeEmail; + + /// Verify email title + /// + /// In en, this message translates to: + /// **'Verify email'** + String get verifyEmail; + + /// Text to indicate that we have sent a mail to the user + /// + /// In en, this message translates to: + /// **'We have sent a mail to {email}'** + String weHaveSendEmailTo(String email); + + /// Message asking user to verify email before password reset + /// + /// In en, this message translates to: + /// **'To reset your password, please verify your email first.'** + String get toResetVerifyEmail; + + /// Message asking user to check inbox and spam folder + /// + /// In en, this message translates to: + /// **'Please check your inbox (and spam) to complete verification'** + String get checkInboxAndSpamFolder; + + /// Hint for entering verification code + /// + /// In en, this message translates to: + /// **'Tap to enter code'** + String get tapToEnterCode; + + /// No description provided for @sendEmail. + /// + /// In en, this message translates to: + /// **'Send email'** + String get sendEmail; + + /// Resend email button label + /// + /// In en, this message translates to: + /// **'Resend email'** + String get resendEmail; + + /// Message when passkey verification is pending + /// + /// In en, this message translates to: + /// **'Verification is still pending'** + String get passKeyPendingVerification; + + /// Login session expired title + /// + /// In en, this message translates to: + /// **'Session expired'** + String get loginSessionExpired; + + /// Login session expired details + /// + /// In en, this message translates to: + /// **'Your session has expired. Please login again.'** + String get loginSessionExpiredDetails; + + /// Passkey authentication title + /// + /// In en, this message translates to: + /// **'Passkey verification'** + String get passkeyAuthTitle; + + /// Waiting for verification message + /// + /// In en, this message translates to: + /// **'Waiting for verification...'** + String get waitingForVerification; + + /// Try again button label + /// + /// In en, this message translates to: + /// **'Try again'** + String get tryAgain; + + /// Check status button label + /// + /// In en, this message translates to: + /// **'Check status'** + String get checkStatus; + + /// Login with TOTP button label + /// + /// In en, this message translates to: + /// **'Login with TOTP'** + String get loginWithTOTP; + + /// Recover account button label + /// + /// In en, this message translates to: + /// **'Recover account'** + String get recoverAccount; + + /// Set password title + /// + /// In en, this message translates to: + /// **'Set password'** + String get setPasswordTitle; + + /// Change password title + /// + /// In en, this message translates to: + /// **'Change password'** + String get changePasswordTitle; + + /// Reset password title + /// + /// In en, this message translates to: + /// **'Reset password'** + String get resetPasswordTitle; + + /// Encryption keys title + /// + /// In en, this message translates to: + /// **'Encryption keys'** + String get encryptionKeys; + + /// Prompt to enter password for encryption + /// + /// In en, this message translates to: + /// **'Enter a password we can use to encrypt your data'** + String get enterPasswordToEncrypt; + + /// Prompt to enter new password for encryption + /// + /// In en, this message translates to: + /// **'Enter a new password we can use to encrypt your data'** + String get enterNewPasswordToEncrypt; + + /// Warning about password storage + /// + /// In en, this message translates to: + /// **'We don\'t store this password, so if you forget, we cannot decrypt your data'** + String get passwordWarning; + + /// How it works button label + /// + /// In en, this message translates to: + /// **'How it works'** + String get howItWorks; + + /// Generating encryption keys message + /// + /// In en, this message translates to: + /// **'Generating encryption keys...'** + String get generatingEncryptionKeys; + + /// Password changed successfully message + /// + /// In en, this message translates to: + /// **'Password changed successfully'** + String get passwordChangedSuccessfully; + + /// Sign out from other devices title + /// + /// In en, this message translates to: + /// **'Sign out from other devices'** + String get signOutFromOtherDevices; + + /// Sign out other devices explanation + /// + /// In en, this message translates to: + /// **'If you think someone might know your password, you can force all other devices using your account to sign out.'** + String get signOutOtherBody; + + /// Sign out other devices button label + /// + /// In en, this message translates to: + /// **'Sign out other devices'** + String get signOutOtherDevices; + + /// Do not sign out button label + /// + /// In en, this message translates to: + /// **'Do not sign out'** + String get doNotSignOut; + + /// Generating encryption keys title + /// + /// In en, this message translates to: + /// **'Generating encryption keys...'** + String get generatingEncryptionKeysTitle; + + /// Continue button label + /// + /// In en, this message translates to: + /// **'Continue'** + String get continueLabel; + + /// Insecure device warning title + /// + /// In en, this message translates to: + /// **'Insecure device'** + String get insecureDevice; + + /// Error message for insecure device + /// + /// In en, this message translates to: + /// **'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'** + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease; + + /// Recovery key copied message + /// + /// In en, this message translates to: + /// **'Recovery key copied to clipboard'** + String get recoveryKeyCopiedToClipboard; + + /// Recovery key label + /// + /// In en, this message translates to: + /// **'Recovery key'** + String get recoveryKey; + + /// Recovery key importance explanation + /// + /// In en, this message translates to: + /// **'If you forget your password, the only way you can recover your data is with this key.'** + String get recoveryKeyOnForgotPassword; + + /// Recovery key save description + /// + /// In en, this message translates to: + /// **'We don\'t store this key, please save this 24 word key in a safe place.'** + String get recoveryKeySaveDescription; + + /// Do this later button label + /// + /// In en, this message translates to: + /// **'Do this later'** + String get doThisLater; + + /// Save key button label + /// + /// In en, this message translates to: + /// **'Save key'** + String get saveKey; + + /// Recovery key saved confirmation message + /// + /// In en, this message translates to: + /// **'Recovery key saved in Downloads folder!'** + String get recoveryKeySaved; + + /// No recovery key title + /// + /// In en, this message translates to: + /// **'No recovery key?'** + String get noRecoveryKeyTitle; + + /// Two-factor authentication title + /// + /// In en, this message translates to: + /// **'Two-factor authentication'** + String get twoFactorAuthTitle; + + /// Hint for entering 2FA code + /// + /// In en, this message translates to: + /// **'Enter the 6-digit code from\nyour authenticator app'** + String get enterCodeHint; + + /// Lost device title + /// + /// In en, this message translates to: + /// **'Lost device?'** + String get lostDeviceTitle; + + /// Hint for entering recovery key + /// + /// In en, this message translates to: + /// **'Enter your recovery key'** + String get enterRecoveryKeyHint; + + /// Recover button label + /// + /// In en, this message translates to: + /// **'Recover'** + String get recover; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index b827fd5378..6f5bd3a754 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -116,4 +116,332 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 304e256092..6994c4632e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -116,4 +116,332 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index 686cafd26a..c6cd8292b5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -116,4 +116,332 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index f6aa408fef..d09be680af 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -116,4 +116,332 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 0602fe735b..85dca55262 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -116,4 +116,332 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 430cdaf61d..d2184d9f2b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -116,4 +116,332 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 14f48212c4..bee2be6dd4 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -116,4 +116,332 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 68f53416f5..b0ed34334f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -116,4 +116,332 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 24d31ecf93..5e8c6a4975 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -116,4 +116,332 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 9a69707f86..9319c940b8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -116,4 +116,332 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index afff907060..69e86cd318 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -116,4 +116,332 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 06e937630a..d2bab7f1ad 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -116,4 +116,332 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index d909e38fe7..67f5932772 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -116,4 +116,332 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index 2f54ceb1a7..08bb0cf8ea 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -116,4 +116,332 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index ccd842665f..311d222d21 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -116,4 +116,332 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index 47a47041d8..fb2a4fb27f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -116,4 +116,332 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 7aab379f90..7b4b2b2f8b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -116,4 +116,332 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 22221537bd..a438cc5105 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -116,4 +116,332 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index ca90e38c6f..3cac1b6faa 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -116,4 +116,332 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index c4fdd62e1e..3c6e98dc13 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -116,4 +116,332 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 906a5a1360..256c086d19 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -116,4 +116,332 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index af1619db26..536d22275a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -115,6 +115,334 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get saveOnlyDescription => 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From 14e570b676b77d12a31bedafe8c9e57924b454a8 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:51:08 +0530 Subject: [PATCH 030/164] Update strings --- .../.filecache | 2 +- .../gen_l10n_inputs_and_outputs.json | 2 +- .../gen_localizations.d | 2 +- .../gen_localizations.stamp | 2 +- .../outputs.json | 2 +- .../strings/lib/l10n/arb/strings_en.arb | 148 ++++++++++++ .../lib/l10n/strings_localizations.dart | 222 ++++++++++++++++++ .../lib/l10n/strings_localizations_ar.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_bg.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_cs.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_da.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_el.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_en.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_es.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_fr.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_id.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_ja.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_ko.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_lt.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_nl.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_pl.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_pt.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_ru.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_sk.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_sr.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_sv.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_tr.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_vi.dart | 120 ++++++++++ .../lib/l10n/strings_localizations_zh.dart | 120 ++++++++++ 29 files changed, 3015 insertions(+), 5 deletions(-) diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache index 2ae62e15bc..15c66e8975 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache @@ -1 +1 @@ -{"version":2,"files":[{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","hash":"ca7ff33b509a7fd08bf183c17865121b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","hash":"996c26108244f5fbcd0d85755ef80bbb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"45c460142089a4ea87608b06ff2628b1"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","hash":"6c3eaaefdb61387a8fb59ff1382628f4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","hash":"8fb8b9881f5c574f6146928e628d8e2b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"25dad41ccf2f92d2c497c7800a1ce45c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"4f2fe3f8c87899e023e72b5a170061aa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","hash":"afefc4f6503e6789d82deeed4c1090e5"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"09e4f926e05cee727e7f9a32cc98b812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","hash":"efd0836034965b6a58b81068b45b2fdb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"7ec1b1400c0b392a9e261e13ac82656f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"5a8a246b080e3003cf8f728d2b10b6f9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","hash":"b1ca0d86c7506706e244476e7017c1a8"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"24b4e737f1bef65c324059937e559819"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","hash":"cb3e5ffb84b723fd099f6291ac5374ef"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","hash":"3aecb1a3a5ea764716303c278ceb94b7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","hash":"8175296d43d021fb17509045e67dea0e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart","hash":"56eb8fc294e50de35c4126fa70816306"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","hash":"56a61967670973048e56649e98abc752"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","hash":"e5022c82bb7c0c9e683421589dabf262"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","hash":"df3028881ead37c84a72186061f16e59"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"68c77aa51f2ba7ce1cc24a7ed22a648b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"ee2dfa2a8e23ad82d27e32d499083189"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"ebfcd08d4c861cc0c5d2d5371e430e56"},{"path":"/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"5ff2d90544438cc086555a06fe71334b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","hash":"b1264e09905a575bbb81f41fca7ed7d0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"f5e6b9da5f1fce53cb7a13ed0ac928d4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"72a20184b7c14aa3bebc1829168ec249"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"b8b1ab1d8d851e1efdaac119ca9aed51"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","hash":"d5b8c067b477d74d2fa41c295ea730c7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","hash":"003c28f62eb2fed28290fbf9063e0feb"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","hash":"6a46248fad78bdec14b8ef3b35a8d702"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","hash":"bf9e6db2ca22d52bdfe962cf63ae83ec"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"a8421a85bad5a6adbff705117f683290"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","hash":"4d089aebcdb149b99acdf4104a781bf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"05da370179c31437bac685fae12ba5e3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"faab6eda68e6fc4782603410686f468a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"b344d11d93750b02904d1ee4b1e40d9e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"55ad5a67f7709ca095b64d46c68feaf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"125a748e9305bf2ff330e307b52ac99f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"5867a832ae87b5e70284d8987d79b4b3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","hash":"7dc1cfda766e41101fa0f954d83bf0f8"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"91fa72e09a0f06350440c87d81ae500c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"1783f60bcbf1764090536df245866c22"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","hash":"b14a4a2269f88c443a2785baad48a812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","hash":"3afb5256fb29b6388dd083e6427aa5dc"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","hash":"3ab484b13262b87bae729b710a69bd70"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"fc62522a963025f3b7fc2c6502083fe7"}]} \ No newline at end of file +{"version":2,"files":[{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","hash":"8f41b63cc709ff8a753e278d4b7a72ae"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"45c460142089a4ea87608b06ff2628b1"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"25dad41ccf2f92d2c497c7800a1ce45c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","hash":"47d6f18fbae448932b49ce2e88a28702"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","hash":"02b0970a33fd94fa1c05af7bb85eb738"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"4f2fe3f8c87899e023e72b5a170061aa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","hash":"8ffd9cff02f848b7dc5e7ccfcb708118"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","hash":"95baffe47d0162033820e1b0347d2a31"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"09e4f926e05cee727e7f9a32cc98b812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","hash":"4382358f933232e0f06720e23c9bc25d"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"e6e5c63b6ca25a0edba29ef2d7caa0f2"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"5a8a246b080e3003cf8f728d2b10b6f9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"24b4e737f1bef65c324059937e559819"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","hash":"1a4e610bf7483c7de90861aec19afce6"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","hash":"f20b14bffe0344d422010d2b83dca0e0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","hash":"0650c239ebf009771c083ad3b131f800"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","hash":"d240b906f482cfe6867c20dd43955650"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","hash":"4956bbcd9300d54327d95a25fb8c9ac0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart","hash":"0bffb4fff11fee135c7d2178da114719"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","hash":"e52d8d8060197d26a6c99b84ad35a206"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"68c77aa51f2ba7ce1cc24a7ed22a648b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"ee2dfa2a8e23ad82d27e32d499083189"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"ebfcd08d4c861cc0c5d2d5371e430e56"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","hash":"593b91ba1704f1c26aff594bfb8c80ca"},{"path":"/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"5ff2d90544438cc086555a06fe71334b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","hash":"7f1fe83b9f37bd55ea049a6a2e5d1abe"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","hash":"6f559a5d6e765b72ffe412c2b9dde308"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"f5e6b9da5f1fce53cb7a13ed0ac928d4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","hash":"5023855b36d4c402d0a54f5a07ff4a75"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"72a20184b7c14aa3bebc1829168ec249"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"b8b1ab1d8d851e1efdaac119ca9aed51"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","hash":"eeccfcc22b8df214f18d4bdd720a6aa4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","hash":"15949a7f7d740883d50afaffed9723b0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"a8421a85bad5a6adbff705117f683290"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","hash":"5cb929b88010b3d89b5e9c9e717fe771"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"05da370179c31437bac685fae12ba5e3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"faab6eda68e6fc4782603410686f468a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"b344d11d93750b02904d1ee4b1e40d9e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","hash":"28c967ee98f78d3372480386856345e9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"55ad5a67f7709ca095b64d46c68feaf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","hash":"fb6ab9b68ee2b2dbc9c5c6924cb6f025"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"125a748e9305bf2ff330e307b52ac99f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"5867a832ae87b5e70284d8987d79b4b3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"91fa72e09a0f06350440c87d81ae500c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"1783f60bcbf1764090536df245866c22"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","hash":"7e8748e5cc7add4cc9cb8aa16f88bf0b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","hash":"6b0c8e15ba72bff3e671eaac675f60fc"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"fc62522a963025f3b7fc2c6502083fe7"}]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json index d0a92c13db..b980c23ac8 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_l10n_inputs_and_outputs.json @@ -1 +1 @@ -{"inputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"]} \ No newline at end of file +{"inputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d index 6d6112802c..fef0e6d5cf 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.d @@ -1 +1 @@ - /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart: /Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb \ No newline at end of file + /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart: /Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb /Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp index 0f4a60ba1e..118ea0a01b 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/gen_localizations.stamp @@ -1 +1 @@ -{"inputs":["/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"]} \ No newline at end of file +{"inputs":["/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json index e6da9694bf..bded8f773a 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/outputs.json @@ -1 +1 @@ -["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_localizations.dart"] \ No newline at end of file +["/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart"] \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 9f5b231302..980994e3c3 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -555,5 +555,153 @@ "recover": "Recover", "@recover": { "description": "Recover button label" + }, + "loggingOut": "Logging out...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Immediately", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "App lock", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Auto lock", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "No system lock found", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "To enable device lock, please setup device passcode or screen lock in your system settings.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Choose between your device's default lock screen and a custom lock screen with a PIN or password.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Device lock", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin lock", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Time after which the app locks after being put in the background", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Hide content", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Hides app content in the app switcher and disables screenshots", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Hides app content in the app switcher", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Too many incorrect attempts", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tap to unlock", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Are you sure you want to logout?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Yes, logout", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Please authenticate to view your secrets", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Next", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Set new password", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Enter PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Set new PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirm", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Re-enter password", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Re-enter PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verify identity", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Not recognized. Try again.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Success", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancel", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Authentication required", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometric required", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Device credentials required", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Device credentials required", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Go to settings", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometric authentication is not set up on your device. Go to 'Settings > Security' to add biometric authentication.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index afe8b80669..ccb3bf5cc9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -931,6 +931,228 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'Recover'** String get recover; + + /// Message shown while logging out + /// + /// In en, this message translates to: + /// **'Logging out...'** + String get loggingOut; + + /// Immediately option for auto lock timing + /// + /// In en, this message translates to: + /// **'Immediately'** + String get immediately; + + /// App lock setting title + /// + /// In en, this message translates to: + /// **'App lock'** + String get appLock; + + /// Auto lock setting title + /// + /// In en, this message translates to: + /// **'Auto lock'** + String get autoLock; + + /// Error when no system lock is found + /// + /// In en, this message translates to: + /// **'No system lock found'** + String get noSystemLockFound; + + /// Instructions for enabling device lock + /// + /// In en, this message translates to: + /// **'To enable device lock, please setup device passcode or screen lock in your system settings.'** + String get deviceLockEnablePreSteps; + + /// Description of app lock feature + /// + /// In en, this message translates to: + /// **'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'** + String get appLockDescription; + + /// Device lock option title + /// + /// In en, this message translates to: + /// **'Device lock'** + String get deviceLock; + + /// PIN lock option title + /// + /// In en, this message translates to: + /// **'Pin lock'** + String get pinLock; + + /// Description of auto lock feature + /// + /// In en, this message translates to: + /// **'Time after which the app locks after being put in the background'** + String get autoLockFeatureDescription; + + /// Hide content setting title + /// + /// In en, this message translates to: + /// **'Hide content'** + String get hideContent; + + /// Description of hide content feature on Android + /// + /// In en, this message translates to: + /// **'Hides app content in the app switcher and disables screenshots'** + String get hideContentDescriptionAndroid; + + /// Description of hide content feature on iOS + /// + /// In en, this message translates to: + /// **'Hides app content in the app switcher'** + String get hideContentDescriptioniOS; + + /// Message shown when too many incorrect attempts are made + /// + /// In en, this message translates to: + /// **'Too many incorrect attempts'** + String get tooManyIncorrectAttempts; + + /// Message prompting user to tap to unlock + /// + /// In en, this message translates to: + /// **'Tap to unlock'** + String get tapToUnlock; + + /// Confirmation message before logout + /// + /// In en, this message translates to: + /// **'Are you sure you want to logout?'** + String get areYouSureYouWantToLogout; + + /// Confirmation button for logout + /// + /// In en, this message translates to: + /// **'Yes, logout'** + String get yesLogout; + + /// Message prompting authentication to view secrets + /// + /// In en, this message translates to: + /// **'Please authenticate to view your secrets'** + String get authToViewSecrets; + + /// Next button label + /// + /// In en, this message translates to: + /// **'Next'** + String get next; + + /// Set new password title + /// + /// In en, this message translates to: + /// **'Set new password'** + String get setNewPassword; + + /// Enter PIN prompt + /// + /// In en, this message translates to: + /// **'Enter PIN'** + String get enterPin; + + /// Set new PIN title + /// + /// In en, this message translates to: + /// **'Set new PIN'** + String get setNewPin; + + /// Confirm button label + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// Re-enter password prompt + /// + /// In en, this message translates to: + /// **'Re-enter password'** + String get reEnterPassword; + + /// Re-enter PIN prompt + /// + /// In en, this message translates to: + /// **'Re-enter PIN'** + String get reEnterPin; + + /// Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Verify identity'** + String get androidBiometricHint; + + /// Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Not recognized. Try again.'** + String get androidBiometricNotRecognized; + + /// Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Success'** + String get androidBiometricSuccess; + + /// Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get androidCancelButton; + + /// Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Authentication required'** + String get androidSignInTitle; + + /// Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Biometric required'** + String get androidBiometricRequiredTitle; + + /// Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Device credentials required'** + String get androidDeviceCredentialsRequiredTitle; + + /// Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side. + /// + /// In en, this message translates to: + /// **'Device credentials required'** + String get androidDeviceCredentialsSetupDescription; + + /// Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'Go to settings'** + String get goToSettings; + + /// Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side. + /// + /// In en, this message translates to: + /// **'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'** + String get androidGoToSettingsDescription; + + /// Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side. + /// + /// In en, this message translates to: + /// **'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'** + String get iOSLockOut; + + /// Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'OK'** + String get iOSOkButton; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 6f5bd3a754..98bdc172d3 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -444,4 +444,124 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 6994c4632e..60dce5783c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -444,4 +444,124 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index c6cd8292b5..d459600125 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -444,4 +444,124 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index d09be680af..a458398a05 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -444,4 +444,124 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 85dca55262..cf2f113dd8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -444,4 +444,124 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index d2184d9f2b..20488e207b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -444,4 +444,124 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index bee2be6dd4..5afce95fa1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -444,4 +444,124 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index b0ed34334f..25b22d66c6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -444,4 +444,124 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 5e8c6a4975..3cd8b4b05a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -444,4 +444,124 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 9319c940b8..6c9e63b325 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -444,4 +444,124 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index 69e86cd318..9fa75484b8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -444,4 +444,124 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index d2bab7f1ad..702d0ab86b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -444,4 +444,124 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 67f5932772..94d1c252cb 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -444,4 +444,124 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index 08bb0cf8ea..c4a477f29e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -444,4 +444,124 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index 311d222d21..d3db542580 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -444,4 +444,124 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index fb2a4fb27f..b944b53c3f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -444,4 +444,124 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 7b4b2b2f8b..99f32a6738 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -444,4 +444,124 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index a438cc5105..079750b0a6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -444,4 +444,124 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 3cac1b6faa..65f7eed158 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -444,4 +444,124 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index 3c6e98dc13..c3c6004baa 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -444,4 +444,124 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 256c086d19..7ba7281dac 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -444,4 +444,124 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 536d22275a..ea01a6f2ce 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -443,6 +443,126 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From 3e9032588e8c1d482c7d9fba3e8a9b7b87181c5d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:51:23 +0530 Subject: [PATCH 031/164] Setup common lockscreen --- .../packages/lock_screen/lib/auth_util.dart | 65 + .../lib/local_authentication_service.dart | 144 ++ .../lock_screen/lib/lock_screen_settings.dart | 253 ++++ .../packages/lock_screen/lib/ui/app_lock.dart | 212 +++ .../lock_screen/lib/ui/custom_pin_keypad.dart | 235 +++ .../lock_screen/lib/ui/lock_screen.dart | 371 +++++ .../lib/ui/lock_screen_auto_lock.dart | 143 ++ .../lib/ui/lock_screen_confirm_password.dart | 186 +++ .../lib/ui/lock_screen_confirm_pin.dart | 212 +++ .../lib/ui/lock_screen_options.dart | 399 +++++ .../lib/ui/lock_screen_password.dart | 250 ++++ .../lock_screen/lib/ui/lock_screen_pin.dart | 285 ++++ mobile/packages/lock_screen/pubspec.lock | 1279 +++++++++++++++++ mobile/packages/lock_screen/pubspec.yaml | 42 + 14 files changed, 4076 insertions(+) create mode 100644 mobile/packages/lock_screen/lib/auth_util.dart create mode 100644 mobile/packages/lock_screen/lib/local_authentication_service.dart create mode 100644 mobile/packages/lock_screen/lib/lock_screen_settings.dart create mode 100644 mobile/packages/lock_screen/lib/ui/app_lock.dart create mode 100644 mobile/packages/lock_screen/lib/ui/custom_pin_keypad.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_options.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_password.dart create mode 100644 mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart create mode 100644 mobile/packages/lock_screen/pubspec.lock create mode 100644 mobile/packages/lock_screen/pubspec.yaml diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart new file mode 100644 index 0000000000..e1574ab8db --- /dev/null +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -0,0 +1,65 @@ +import 'dart:io'; + +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_local_authentication/flutter_local_authentication.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:local_auth_android/local_auth_android.dart'; +import 'package:local_auth_darwin/types/auth_messages_ios.dart'; +import 'package:lock_screen/local_authentication_service.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:logging/logging.dart'; + +Future requestAuthentication( + BuildContext context, + String reason, { + bool isOpeningApp = false, + bool isAuthenticatingForInAppChange = false, +}) async { + Logger("AuthUtil").info("Requesting authentication"); + + final String? savedPin = await LockScreenSettings.instance.getPin(); + final String? savedPassword = await LockScreenSettings.instance.getPassword(); + if (savedPassword != null || savedPin != null) { + return await LocalAuthenticationService.instance + .requestEnteAuthForLockScreen( + context, + savedPin, + savedPassword, + isAuthenticatingOnAppLaunch: isOpeningApp, + isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, + ); + } + if (Platform.isMacOS || Platform.isLinux) { + return await FlutterLocalAuthentication().authenticate(); + } else { + await LocalAuthentication().stopAuthentication(); + final l10n = context.strings; + return await LocalAuthentication().authenticate( + localizedReason: reason, + authMessages: [ + AndroidAuthMessages( + biometricHint: l10n.androidBiometricHint, + biometricNotRecognized: l10n.androidBiometricNotRecognized, + biometricRequiredTitle: l10n.androidBiometricRequiredTitle, + biometricSuccess: l10n.androidBiometricSuccess, + cancelButton: l10n.androidCancelButton, + deviceCredentialsRequiredTitle: + l10n.androidDeviceCredentialsRequiredTitle, + deviceCredentialsSetupDescription: + l10n.androidDeviceCredentialsSetupDescription, + goToSettingsButton: l10n.goToSettings, + goToSettingsDescription: l10n.androidGoToSettingsDescription, + signInTitle: l10n.androidSignInTitle, + ), + IOSAuthMessages( + goToSettingsButton: l10n.goToSettings, + goToSettingsDescription: l10n.goToSettings, + lockOut: l10n.iOSLockOut, + // cancelButton default value is "Ok" + cancelButton: l10n.iOSOkButton, + ), + ], + ); + } +} diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart new file mode 100644 index 0000000000..2ff4b0b673 --- /dev/null +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -0,0 +1,144 @@ +import 'dart:io'; + +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_local_authentication/flutter_local_authentication.dart'; +import 'package:local_auth/local_auth.dart'; +import 'package:lock_screen/auth_util.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:lock_screen/ui/app_lock.dart'; +import 'package:lock_screen/ui/lock_screen_password.dart'; +import 'package:lock_screen/ui/lock_screen_pin.dart'; +import 'package:logging/logging.dart'; + +class LocalAuthenticationService { + LocalAuthenticationService._privateConstructor(); + static final LocalAuthenticationService instance = + LocalAuthenticationService._privateConstructor(); + final logger = Logger((LocalAuthenticationService).toString()); + int lastAuthTime = 0; + + Future requestLocalAuthentication( + BuildContext context, + String infoMessage, + ) async { + if (kDebugMode) { + // if last auth time is less than 60 seconds, don't ask for auth again + if (lastAuthTime != 0 && + DateTime.now().millisecondsSinceEpoch - lastAuthTime < 60000) { + return true; + } + } + if (await isLocalAuthSupportedOnDevice() || + LockScreenSettings.instance.getIsAppLockSet()) { + AppLock.of(context)!.setEnabled(false); + final result = await requestAuthentication( + context, + infoMessage, + isAuthenticatingForInAppChange: true, + ); + AppLock.of(context)!.setEnabled( + await LockScreenSettings.instance.shouldShowLockScreen(), + ); + if (!result) { + showToast(context, infoMessage); + return false; + } else { + lastAuthTime = DateTime.now().millisecondsSinceEpoch; + return true; + } + } + return true; + } + + Future requestEnteAuthForLockScreen( + BuildContext context, + String? savedPin, + String? savedPassword, { + bool isAuthenticatingOnAppLaunch = false, + bool isAuthenticatingForInAppChange = false, + }) async { + if (savedPassword != null) { + final result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LockScreenPassword( + isChangingLockScreenSettings: true, + isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, + isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch, + authPass: savedPassword, + ); + }, + ), + ); + if (result) { + return true; + } + } + if (savedPin != null) { + final result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LockScreenPin( + isChangingLockScreenSettings: true, + isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, + isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch, + authPin: savedPin, + ); + }, + ), + ); + if (result) { + return true; + } + } + return false; + } + + Future requestLocalAuthForLockScreen( + BuildContext context, + bool shouldEnableLockScreen, + String infoMessage, + String errorDialogContent, [ + String errorDialogTitle = "", + ]) async { + if (await isLocalAuthSupportedOnDevice()) { + AppLock.of(context)!.disable(); + final result = await requestAuthentication( + context, + infoMessage, + ); + if (result) { + AppLock.of(context)!.setEnabled(shouldEnableLockScreen); + await LockScreenSettings.instance + .setSystemLockScreen(shouldEnableLockScreen); + return true; + } else { + AppLock.of(context)!.setEnabled( + await LockScreenSettings.instance.shouldShowLockScreen(), + ); + } + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + errorDialogTitle, + errorDialogContent, + ); + } + return false; + } + + Future isLocalAuthSupportedOnDevice() async { + try { + return Platform.isLinux + ? await FlutterLocalAuthentication().canAuthenticate() + : await LocalAuthentication().isDeviceSupported(); + } on MissingPluginException { + return false; + } + } +} diff --git a/mobile/packages/lock_screen/lib/lock_screen_settings.dart b/mobile/packages/lock_screen/lib/lock_screen_settings.dart new file mode 100644 index 0000000000..99a41478bf --- /dev/null +++ b/mobile/packages/lock_screen/lib/lock_screen_settings.dart @@ -0,0 +1,253 @@ +import "dart:convert"; +import "dart:io"; +import "dart:typed_data"; + +import "package:ente_configuration/base_configuration.dart"; +import "package:ente_utils/platform_util.dart"; +import "package:ente_events/event_bus.dart"; +import "package:ente_events/models/signed_out_event.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:flutter/material.dart"; +import "package:flutter_secure_storage/flutter_secure_storage.dart"; +import "package:privacy_screen/privacy_screen.dart"; +import "package:shared_preferences/shared_preferences.dart"; + +class LockScreenSettings { + LockScreenSettings._privateConstructor(); + + static final LockScreenSettings instance = + LockScreenSettings._privateConstructor(); + static const password = "ls_password"; + static const pin = "ls_pin"; + static const saltKey = "ls_salt"; + static const keyInvalidAttempts = "ls_invalid_attempts"; + static const lastInvalidAttemptTime = "ls_last_invalid_attempt_time"; + static const autoLockTime = "ls_auto_lock_time"; + static const keyHideAppContent = "ls_hide_app_content"; + static const keyAppLockSet = "ls_is_app_lock_set"; + static const keyHasMigratedLockScreenChanges = + "ls_has_migrated_lock_screen_changes"; + static const keyShowOfflineModeWarning = "ls_show_offline_mode_warning"; + static const keyShouldShowLockScreen = "should_show_lock_screen"; + static const String kIsLightMode = "is_light_mode"; + + final List autoLockDurations = const [ + Duration(milliseconds: 650), + Duration(seconds: 5), + Duration(seconds: 15), + Duration(minutes: 1), + Duration(minutes: 5), + Duration(minutes: 30), + ]; + + late BaseConfiguration _config; + late SharedPreferences _preferences; + late FlutterSecureStorage _secureStorage; + + Future init(BaseConfiguration config) async { + _config = config; + _secureStorage = const FlutterSecureStorage(); + _preferences = await SharedPreferences.getInstance(); + + ///Workaround for privacyScreen not working when app is killed and opened. + await setHideAppContent(getShouldHideAppContent()); + + /// Function to Check if the migration for lock screen changes has + /// already been done by checking a stored boolean value. + await runLockScreenChangesMigration(); + + await _clearLsDataInKeychainIfFreshInstall(); + + Bus.instance.on().listen((event) { + removePinAndPassword(); + }); + } + + Future setOfflineModeWarningStatus(bool value) async { + await _preferences.setBool(keyShowOfflineModeWarning, value); + } + + bool getOfflineModeWarningStatus() { + return _preferences.getBool(keyShowOfflineModeWarning) ?? true; + } + + Future runLockScreenChangesMigration() async { + if (_preferences.getBool(keyHasMigratedLockScreenChanges) != null) { + return; + } + + final bool passwordEnabled = await isPasswordSet(); + final bool pinEnabled = await isPinSet(); + final bool systemLockEnabled = shouldShowSystemLockScreen(); + + if (passwordEnabled || pinEnabled || systemLockEnabled) { + await setAppLockEnabled(true); + } + + await _preferences.setBool(keyHasMigratedLockScreenChanges, true); + } + + Future setLightMode(bool isLightMode) async { + if (isLightMode != (_preferences.getBool(kIsLightMode) ?? true)) { + await _preferences.setBool(kIsLightMode, isLightMode); + } + } + + Future setHideAppContent(bool hideContent) async { + if (PlatformUtil.isDesktop()) return; + final bool isLightMode = _preferences.getBool(kIsLightMode) ?? true; + !hideContent + ? PrivacyScreen.instance.disable() + : await PrivacyScreen.instance.enable( + iosOptions: const PrivacyIosOptions( + enablePrivacy: true, + ), + androidOptions: const PrivacyAndroidOptions( + enableSecure: true, + ), + backgroundColor: + isLightMode ? const Color(0xffffffff) : const Color(0xff000000), + blurEffect: isLightMode + ? PrivacyBlurEffect.extraLight + : PrivacyBlurEffect.extraLight, + ); + await _preferences.setBool(keyHideAppContent, hideContent); + } + + bool getShouldHideAppContent() { + return _preferences.getBool(keyHideAppContent) ?? true; + } + + Future setAutoLockTime(Duration duration) async { + await _preferences.setInt(autoLockTime, duration.inMilliseconds); + } + + int getAutoLockTime() { + return _preferences.getInt(autoLockTime) ?? 5000; + } + + Future setLastInvalidAttemptTime(int time) async { + await _preferences.setInt(lastInvalidAttemptTime, time); + } + + int getlastInvalidAttemptTime() { + return _preferences.getInt(lastInvalidAttemptTime) ?? 0; + } + + int getInvalidAttemptCount() { + return _preferences.getInt(keyInvalidAttempts) ?? 0; + } + + Future setInvalidAttemptCount(int count) async { + await _preferences.setInt(keyInvalidAttempts, count); + } + + Future setAppLockEnabled(bool value) async { + await _preferences.setBool(keyAppLockSet, value); + } + + bool getIsAppLockSet() { + return _preferences.getBool(keyAppLockSet) ?? false; + } + + static Uint8List _generateSalt() { + return sodium.randombytes.buf(sodium.crypto.pwhash.saltBytes); + } + + Future setPin(String userPin) async { + await _secureStorage.delete(key: saltKey); + final salt = _generateSalt(); + + final hash = cryptoPwHash( + utf8.encode(userPin), + salt, + sodium.crypto.pwhash.memLimitInteractive, + sodium.crypto.pwhash.opsLimitSensitive, + sodium, + ); + final String saltPin = base64Encode(salt); + final String hashedPin = base64Encode(hash); + + await _secureStorage.write(key: saltKey, value: saltPin); + await _secureStorage.write(key: pin, value: hashedPin); + await _secureStorage.delete(key: password); + + return; + } + + Future getSalt() async { + final String? salt = await _secureStorage.read(key: saltKey); + if (salt == null) return null; + return base64Decode(salt); + } + + Future getPin() async { + return _secureStorage.read(key: pin); + } + + Future setPassword(String pass) async { + await _secureStorage.delete(key: saltKey); + final salt = _generateSalt(); + + final hash = cryptoPwHash( + utf8.encode(pass), + salt, + sodium.crypto.pwhash.memLimitInteractive, + sodium.crypto.pwhash.opsLimitSensitive, + sodium, + ); + + await _secureStorage.write(key: saltKey, value: base64Encode(salt)); + await _secureStorage.write(key: password, value: base64Encode(hash)); + await _secureStorage.delete(key: pin); + + return; + } + + Future getPassword() async { + return _secureStorage.read(key: password); + } + + Future removePinAndPassword() async { + await _secureStorage.delete(key: saltKey); + await _secureStorage.delete(key: pin); + await _secureStorage.delete(key: password); + } + + Future isPinSet() async { + return await _secureStorage.containsKey(key: pin); + } + + Future isPasswordSet() async { + return await _secureStorage.containsKey(key: password); + } + + Future shouldShowLockScreen() async { + final bool isPin = await isPinSet(); + final bool isPass = await isPasswordSet(); + return isPin || isPass || shouldShowSystemLockScreen(); + } + + bool shouldShowSystemLockScreen() { + if (_preferences.containsKey(keyShouldShowLockScreen)) { + return _preferences.getBool(keyShouldShowLockScreen)!; + } else { + return false; + } + } + + Future setSystemLockScreen(bool value) { + return _preferences.setBool(keyShouldShowLockScreen, value); + } + + // If the app was uninstalled (without logging out if it was used with + // backups), keychain items of the app persist in the keychain. To avoid using + // old keychain items, we delete them on reinstall. + Future _clearLsDataInKeychainIfFreshInstall() async { + if ((Platform.isIOS || Platform.isMacOS) && !_config.isLoggedIn()) { + await _secureStorage.delete(key: password); + await _secureStorage.delete(key: pin); + await _secureStorage.delete(key: saltKey); + } + } +} diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart new file mode 100644 index 0000000000..4e93ce5ec1 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -0,0 +1,212 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; + +/// A widget which handles app lifecycle events for showing and hiding a lock screen. +/// This should wrap around a `MyApp` widget (or equivalent). +/// +/// [lockScreen] is a [Widget] which should be a screen for handling login logic and +/// calling `AppLock.of(context).didUnlock();` upon a successful login. +/// +/// [builder] is a [Function] taking an [Object] as its argument and should return a +/// [Widget]. The [Object] argument is provided by the [lockScreen] calling +/// `AppLock.of(context).didUnlock();` with an argument. [Object] can then be injected +/// in to your `MyApp` widget (or equivalent). +/// +/// [enabled] determines wether or not the [lockScreen] should be shown on app launch +/// and subsequent app pauses. This can be changed later on using `AppLock.of(context).enable();`, +/// `AppLock.of(context).disable();` or the convenience method `AppLock.of(context).setEnabled(enabled);` +/// using a bool argument. +/// +/// [backgroundLockLatency] determines how much time is allowed to pass when +/// the app is in the background state before the [lockScreen] widget should be +/// shown upon returning. It defaults to instantly. +/// + +// ignore_for_file: unnecessary_this, library_private_types_in_public_api +class AppLock extends StatefulWidget { + final Widget Function(Object?) builder; + final Widget lockScreen; + final bool enabled; + final Duration backgroundLockLatency; + final ThemeData? darkTheme; + final ThemeData? lightTheme; + final ThemeMode savedThemeMode; + final Locale? locale; + final List? supportedLocales; + final List> localizationsDelegates; + final LocaleListResolutionCallback? localeListResolutionCallback; + + const AppLock({ + super.key, + required this.builder, + required this.lockScreen, + required this.savedThemeMode, + required this.supportedLocales, + required this.localizationsDelegates, + required this.localeListResolutionCallback, + this.enabled = true, + this.locale, + this.backgroundLockLatency = const Duration(seconds: 0), + this.darkTheme, + this.lightTheme, + }); + + static _AppLockState? of(BuildContext context) => + context.findAncestorStateOfType<_AppLockState>(); + + @override + State createState() => _AppLockState(); +} + +class _AppLockState extends State with WidgetsBindingObserver { + static final GlobalKey _navigatorKey = GlobalKey(); + + late bool _didUnlockForAppLaunch; + late bool _isLocked; + late bool _enabled; + + Timer? _backgroundLockLatencyTimer; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addObserver(this); + + this._didUnlockForAppLaunch = !this.widget.enabled; + this._isLocked = false; + this._enabled = this.widget.enabled; + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (!this._enabled) { + return; + } + + if (state == AppLifecycleState.paused && + (!this._isLocked && this._didUnlockForAppLaunch)) { + this._backgroundLockLatencyTimer = Timer( + Duration( + milliseconds: LockScreenSettings.instance.getAutoLockTime(), + ), + () => this.showLockScreen(), + ); + } + + if (state == AppLifecycleState.resumed) { + this._backgroundLockLatencyTimer?.cancel(); + } + + super.didChangeAppLifecycleState(state); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + + this._backgroundLockLatencyTimer?.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: this.widget.enabled ? this._lockScreen : this.widget.builder(null), + navigatorKey: _navigatorKey, + themeMode: widget.savedThemeMode, + theme: widget.lightTheme, + darkTheme: widget.darkTheme, + locale: widget.locale, + supportedLocales: + widget.supportedLocales ?? const [Locale('en', 'US')], + localeListResolutionCallback: widget.localeListResolutionCallback, + localizationsDelegates: widget.localizationsDelegates, + onGenerateRoute: (settings) { + switch (settings.name) { + case '/lock-screen': + return PageRouteBuilder( + pageBuilder: (_, __, ___) => this._lockScreen, + ); + case '/unlocked': + return PageRouteBuilder( + pageBuilder: (_, __, ___) => + this.widget.builder(settings.arguments), + ); + } + return PageRouteBuilder(pageBuilder: (_, __, ___) => this._lockScreen); + }, + ); + } + + Widget get _lockScreen { + return PopScope( + child: this.widget.lockScreen, + canPop: false, + ); + } + + /// Causes `AppLock` to either pop the [lockScreen] if the app is already running + /// or instantiates widget returned from the [builder] method if the app is cold + /// launched. + /// + /// [args] is an optional argument which will get passed to the [builder] method + /// when built. Use this when you want to inject objects created from the + /// [lockScreen] in to the rest of your app so you can better guarantee that some + /// objects, services or databases are already instantiated before using them. + void didUnlock([Object? args]) { + if (this._didUnlockForAppLaunch) { + this._didUnlockOnAppPaused(); + } else { + this._didUnlockOnAppLaunch(args); + } + } + + /// Makes sure that [AppLock] shows the [lockScreen] on subsequent app pauses if + /// [enabled] is true of makes sure it isn't shown on subsequent app pauses if + /// [enabled] is false. + /// + /// This is a convenience method for calling the [enable] or [disable] method based + /// on [enabled]. + void setEnabled(bool enabled) { + if (enabled) { + this.enable(); + } else { + this.disable(); + } + } + + /// Makes sure that [AppLock] shows the [lockScreen] on subsequent app pauses. + void enable() { + setState(() { + this._enabled = true; + }); + } + + /// Makes sure that [AppLock] doesn't show the [lockScreen] on subsequent app pauses. + void disable() { + setState(() { + this._enabled = false; + }); + } + + /// Manually show the [lockScreen]. + Future showLockScreen() { + this._isLocked = true; + return _navigatorKey.currentState!.pushNamed('/lock-screen'); + } + + void _didUnlockOnAppLaunch(Object? args) { + this._didUnlockForAppLaunch = true; + _navigatorKey.currentState! + .pushReplacementNamed('/unlocked', arguments: args); + } + + void _didUnlockOnAppPaused() { + this._isLocked = false; + _navigatorKey.currentState!.pop(); + } +} diff --git a/mobile/packages/lock_screen/lib/ui/custom_pin_keypad.dart b/mobile/packages/lock_screen/lib/ui/custom_pin_keypad.dart new file mode 100644 index 0000000000..f6fdf9c3e0 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/custom_pin_keypad.dart @@ -0,0 +1,235 @@ +import "package:ente_ui/theme/ente_theme.dart"; +import "package:flutter/material.dart"; + +class CustomPinKeypad extends StatelessWidget { + final TextEditingController controller; + const CustomPinKeypad({required this.controller, super.key}); + + @override + Widget build(BuildContext context) { + return SafeArea( + child: Container( + padding: const EdgeInsets.all(2), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + color: getEnteColorScheme(context).strokeFainter, + child: Column( + children: [ + Row( + children: [ + _Button( + text: '', + number: '1', + onTap: () { + _onKeyTap('1'); + }, + ), + _Button( + text: "ABC", + number: '2', + onTap: () { + _onKeyTap('2'); + }, + ), + _Button( + text: "DEF", + number: '3', + onTap: () { + _onKeyTap('3'); + }, + ), + ], + ), + Row( + children: [ + _Button( + number: '4', + text: "GHI", + onTap: () { + _onKeyTap('4'); + }, + ), + _Button( + number: '5', + text: 'JKL', + onTap: () { + _onKeyTap('5'); + }, + ), + _Button( + number: '6', + text: 'MNO', + onTap: () { + _onKeyTap('6'); + }, + ), + ], + ), + Row( + children: [ + _Button( + number: '7', + text: 'PQRS', + onTap: () { + _onKeyTap('7'); + }, + ), + _Button( + number: '8', + text: 'TUV', + onTap: () { + _onKeyTap('8'); + }, + ), + _Button( + number: '9', + text: 'WXYZ', + onTap: () { + _onKeyTap('9'); + }, + ), + ], + ), + Row( + children: [ + const _Button( + number: '', + text: '', + muteButton: true, + onTap: null, + ), + _Button( + number: '0', + text: '', + onTap: () { + _onKeyTap('0'); + }, + ), + _Button( + number: '', + text: '', + icon: const Icon(Icons.backspace_outlined), + onTap: () { + _onBackspace(); + }, + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + void _onKeyTap(String number) { + controller.text += number; + return; + } + + void _onBackspace() { + if (controller.text.isNotEmpty) { + controller.text = + controller.text.substring(0, controller.text.length - 1); + } + return; + } +} + +class _Button extends StatefulWidget { + final String number; + final String text; + final VoidCallback? onTap; + final bool muteButton; + final Widget? icon; + + const _Button({ + required this.number, + required this.text, + this.muteButton = false, + required this.onTap, + this.icon, + }); + + @override + State<_Button> createState() => _ButtonState(); +} + +class _ButtonState extends State<_Button> { + bool isPressed = false; + + void _onTapDown(TapDownDetails details) { + setState(() { + isPressed = true; + }); + } + + void _onTapUp(TapUpDetails details) async { + setState(() { + isPressed = false; + }); + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + return Expanded( + child: GestureDetector( + onTap: widget.onTap, + onTapDown: _onTapDown, + onTapUp: _onTapUp, + child: AnimatedContainer( + duration: const Duration(milliseconds: 100), + curve: Curves.easeOut, + child: Container( + margin: const EdgeInsets.all(4), + decoration: BoxDecoration( + shape: BoxShape.rectangle, + borderRadius: BorderRadius.circular(6), + color: isPressed + ? colorScheme.backgroundElevated + : widget.muteButton + ? colorScheme.fillFaintPressed + : widget.icon == null + ? colorScheme.backgroundElevated2 + : null, + ), + child: Center( + child: widget.muteButton + ? const SizedBox.shrink() + : widget.icon != null + ? Container( + padding: const EdgeInsets.symmetric( + horizontal: 4, + vertical: 10, + ), + child: widget.icon, + ) + : Container( + padding: const EdgeInsets.all(4), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.number, + style: textTheme.h3, + ), + Text( + widget.text, + style: textTheme.tinyBold, + ), + ], + ), + ), + ), + ), + ), + ), + ); + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart new file mode 100644 index 0000000000..74cbcfc554 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -0,0 +1,371 @@ +import 'dart:io'; +import 'dart:math'; + +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:flutter_animate/flutter_animate.dart'; +import 'package:lock_screen/auth_util.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:lock_screen/ui/app_lock.dart'; +import 'package:logging/logging.dart'; + +class LockScreen extends StatefulWidget { + final BaseConfiguration config; + + const LockScreen( + this.config, { + super.key, + }); + + @override + State createState() => _LockScreenState(); +} + +class _LockScreenState extends State with WidgetsBindingObserver { + final _logger = Logger("LockScreen"); + bool _isShowingLockScreen = false; + bool _hasPlacedAppInBackground = false; + bool _hasAuthenticationFailed = false; + int? lastAuthenticatingTime; + bool isTimerRunning = false; + int lockedTimeInSeconds = 0; + int invalidAttemptCount = 0; + int remainingTimeInSeconds = 0; + final _lockscreenSetting = LockScreenSettings.instance; + late Brightness _platformBrightness; + + @override + void initState() { + _logger.info("initiatingState"); + super.initState(); + invalidAttemptCount = _lockscreenSetting.getInvalidAttemptCount(); + WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + _showLockScreen(source: "postFrameInit"); + }); + _platformBrightness = + SchedulerBinding.instance.platformDispatcher.platformBrightness; + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + return Scaffold( + appBar: AppBar( + elevation: 0, + leading: widget.config.isLoggedIn() + ? IconButton( + icon: const Icon(Icons.logout_outlined), + color: Theme.of(context).iconTheme.color, + onPressed: () { + _onLogoutTapped(context); + }, + ) + : const SizedBox.shrink(), + ), + body: GestureDetector( + onTap: () { + isTimerRunning ? null : _showLockScreen(source: "tap"); + }, + child: Container( + decoration: BoxDecoration( + image: DecorationImage( + opacity: _platformBrightness == Brightness.light ? 0.08 : 0.12, + image: const ExactAssetImage( + 'assets/loading_photos_background.png', + ), + fit: BoxFit.cover, + ), + ), + child: Center( + child: Column( + children: [ + const Spacer(), + SizedBox( + height: 120, + width: 120, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 82, + height: 82, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Colors.grey.shade500.withOpacity(0.2), + Colors.grey.shade50.withOpacity(0.1), + Colors.grey.shade400.withOpacity(0.2), + Colors.grey.shade300.withOpacity(0.4), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorTheme.backgroundBase, + ), + ), + ), + ), + SizedBox( + height: 75, + width: 75, + child: TweenAnimationBuilder( + tween: Tween( + begin: isTimerRunning ? 0 : 1, + end: isTimerRunning + ? _getFractionOfTimeElapsed() + : 1, + ), + duration: const Duration(seconds: 1), + builder: (context, value, _) => + CircularProgressIndicator( + backgroundColor: colorTheme.fillFaintPressed, + value: value, + color: colorTheme.primary400, + strokeWidth: 1.5, + ), + ), + ), + Icon( + Icons.lock, + size: 30, + color: colorTheme.textBase, + ), + ], + ), + ), + const Spacer(), + isTimerRunning + ? Stack( + alignment: Alignment.center, + children: [ + Text( + context.strings.tooManyIncorrectAttempts, + style: textTheme.small, + ) + .animate( + delay: const Duration(milliseconds: 2000), + ) + .fadeOut( + duration: 400.ms, + curve: Curves.easeInOutCirc, + ), + Text( + _formatTime(remainingTimeInSeconds), + style: textTheme.small, + ) + .animate( + delay: const Duration(milliseconds: 2250), + ) + .fadeIn( + duration: 400.ms, + curve: Curves.easeInOutCirc, + ), + ], + ) + : GestureDetector( + onTap: () => _showLockScreen(source: "tap"), + child: Text( + context.strings.tapToUnlock, + style: textTheme.small, + ), + ), + const Padding( + padding: EdgeInsets.only(bottom: 24), + ), + ], + ), + ), + ), + ), + ); + } + + void _onLogoutTapped(BuildContext context) { + showChoiceActionSheet( + context, + title: context.strings.areYouSureYouWantToLogout, + firstButtonLabel: context.strings.yesLogout, + isCritical: true, + firstButtonOnTap: () async { + await UserService.instance.logout(context); + // To start the app afresh, resetting all state. + Process.killPid(pid, ProcessSignal.sigkill); + }, + ); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + _logger.info(state.toString()); + if (state == AppLifecycleState.resumed && !_isShowingLockScreen) { + // This is triggered either when the lock screen is dismissed or when + // the app is brought to foreground + _hasPlacedAppInBackground = false; + final bool didAuthInLast5Seconds = lastAuthenticatingTime != null && + DateTime.now().millisecondsSinceEpoch - lastAuthenticatingTime! < + 5000; + if (!_hasAuthenticationFailed && !didAuthInLast5Seconds) { + // Show the lock screen again only if the app is resuming from the + // background, and not when the lock screen was explicitly dismissed + if (_lockscreenSetting.getlastInvalidAttemptTime() > + DateTime.now().millisecondsSinceEpoch && + !_isShowingLockScreen) { + final int time = (_lockscreenSetting.getlastInvalidAttemptTime() - + DateTime.now().millisecondsSinceEpoch) ~/ + 1000; + Future.delayed(Duration.zero, () { + startLockTimer(time); + _showLockScreen(source: "lifeCycle"); + }); + } + } else { + _hasAuthenticationFailed = false; // Reset failure state + } + } else if (state == AppLifecycleState.paused || + state == AppLifecycleState.inactive) { + // This is triggered either when the lock screen pops up or when + // the app is pushed to background + if (!_isShowingLockScreen) { + _hasPlacedAppInBackground = true; + _hasAuthenticationFailed = false; // reset failure state + } + } + } + + @override + void dispose() { + _logger.info('disposing'); + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + Future startLockTimer(int timeInSeconds) async { + if (isTimerRunning) { + return; + } + + setState(() { + isTimerRunning = true; + remainingTimeInSeconds = timeInSeconds; + }); + + while (remainingTimeInSeconds > 0) { + await Future.delayed(const Duration(seconds: 1)); + setState(() { + remainingTimeInSeconds--; + }); + } + + setState(() { + isTimerRunning = false; + }); + } + + double _getFractionOfTimeElapsed() { + final int totalLockedTime = + lockedTimeInSeconds = pow(2, invalidAttemptCount - 5).toInt() * 30; + if (remainingTimeInSeconds == 0) return 1; + + return 1 - remainingTimeInSeconds / totalLockedTime; + } + + String _formatTime(int seconds) { + final int hours = seconds ~/ 3600; + final int minutes = (seconds % 3600) ~/ 60; + final int remainingSeconds = seconds % 60; + + if (hours > 0) { + return "${hours}h ${minutes}m"; + } else if (minutes > 0) { + return "${minutes}m ${remainingSeconds}s"; + } else { + return "${remainingSeconds}s"; + } + } + + Future _autoLogoutOnMaxInvalidAttempts() async { + _logger.info("Auto logout on max invalid attempts"); + Navigator.of(context, rootNavigator: true).pop('dialog'); + Navigator.of(context).popUntil((route) => route.isFirst); + final dialog = createProgressDialog(context, context.strings.loggingOut); + await dialog.show(); + await widget.config.logout(); + await dialog.hide(); + } + + Future _showLockScreen({String source = ''}) async { + final int currentTimestamp = DateTime.now().millisecondsSinceEpoch; + _logger.info("Showing lock screen $source $currentTimestamp"); + try { + if (currentTimestamp < _lockscreenSetting.getlastInvalidAttemptTime() && + !_isShowingLockScreen) { + final int remainingTime = + (_lockscreenSetting.getlastInvalidAttemptTime() - + currentTimestamp) ~/ + 1000; + + await startLockTimer(remainingTime); + } + _isShowingLockScreen = true; + final result = isTimerRunning + ? false + : await requestAuthentication( + context, + context.strings.authToViewSecrets, + isOpeningApp: true, + ); + _logger.finest("LockScreen Result $result $currentTimestamp"); + _isShowingLockScreen = false; + if (result) { + lastAuthenticatingTime = DateTime.now().millisecondsSinceEpoch; + AppLock.of(context)?.didUnlock(); + await _lockscreenSetting.setInvalidAttemptCount(0); + setState(() { + lockedTimeInSeconds = 15; + isTimerRunning = false; + }); + } else { + if (!_hasPlacedAppInBackground) { + // Treat this as a failure only if user did not explicitly + // put the app in background + if (_lockscreenSetting.getInvalidAttemptCount() > 4 && + invalidAttemptCount != + _lockscreenSetting.getInvalidAttemptCount()) { + invalidAttemptCount = _lockscreenSetting.getInvalidAttemptCount(); + + if (invalidAttemptCount > 9) { + await _autoLogoutOnMaxInvalidAttempts(); + return; + } + + lockedTimeInSeconds = pow(2, invalidAttemptCount - 5).toInt() * 30; + await _lockscreenSetting.setLastInvalidAttemptTime( + DateTime.now().millisecondsSinceEpoch + + lockedTimeInSeconds * 1000, + ); + await startLockTimer(lockedTimeInSeconds); + } + _hasAuthenticationFailed = true; + _logger.info("Authentication failed"); + } + } + } catch (e, s) { + _isShowingLockScreen = false; + _logger.severe(e, s); + } + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart new file mode 100644 index 0000000000..dfdc11b8f5 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -0,0 +1,143 @@ +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/captioned_text_widget.dart'; +import 'package:ente_ui/components/divider_widget.dart'; +import 'package:ente_ui/components/menu_item_widget.dart'; +import 'package:ente_ui/components/separators.dart'; +import 'package:ente_ui/components/title_bar_title_widget.dart'; +import 'package:ente_ui/components/title_bar_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; + +class LockScreenAutoLock extends StatefulWidget { + const LockScreenAutoLock({super.key}); + + @override + State createState() => _LockScreenAutoLockState(); +} + +class _LockScreenAutoLockState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + primary: false, + slivers: [ + TitleBarWidget( + flexibleSpaceTitle: TitleBarTitleWidget( + title: context.strings.autoLock, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return const Padding( + padding: EdgeInsets.symmetric( + horizontal: 16, + vertical: 20, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + children: [ + ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(8)), + child: AutoLockItems(), + ), + ], + ), + ], + ), + ); + }, + childCount: 1, + ), + ), + ], + ), + ); + } +} + +class AutoLockItems extends StatefulWidget { + const AutoLockItems({super.key}); + + @override + State createState() => _AutoLockItemsState(); +} + +class _AutoLockItemsState extends State { + final autoLockDurations = LockScreenSettings.instance.autoLockDurations; + List items = []; + Duration currentAutoLockTime = const Duration(seconds: 5); + + @override + void initState() { + for (Duration autoLockDuration in autoLockDurations) { + if (autoLockDuration.inMilliseconds == + LockScreenSettings.instance.getAutoLockTime()) { + currentAutoLockTime = autoLockDuration; + break; + } + } + super.initState(); + } + + @override + Widget build(BuildContext context) { + items.clear(); + for (Duration autoLockDuration in autoLockDurations) { + items.add( + _menuItemForPicker(autoLockDuration), + ); + } + items = addSeparators( + items, + DividerWidget( + dividerType: DividerType.menuNoIcon, + bgColor: getEnteColorScheme(context).fillFaint, + ), + ); + return Column( + mainAxisSize: MainAxisSize.min, + children: items, + ); + } + + Widget _menuItemForPicker(Duration autoLockTime) { + return MenuItemWidget( + key: ValueKey(autoLockTime), + menuItemColor: getEnteColorScheme(context).fillFaint, + captionedTextWidget: CaptionedTextWidget( + title: _formatTime(autoLockTime), + ), + trailingIcon: currentAutoLockTime == autoLockTime ? Icons.check : null, + alignCaptionedTextToLeft: true, + isTopBorderRadiusRemoved: true, + isBottomBorderRadiusRemoved: true, + showOnlyLoadingState: true, + onTap: () async { + await LockScreenSettings.instance.setAutoLockTime(autoLockTime).then( + (value) => { + setState(() { + currentAutoLockTime = autoLockTime; + }), + }, + ); + }, + ); + } + + String _formatTime(Duration duration) { + if (duration.inHours != 0) { + return "${duration.inHours}hr"; + } else if (duration.inMinutes != 0) { + return "${duration.inMinutes}m"; + } else if (duration.inSeconds != 0) { + return "${duration.inSeconds}s"; + } else { + return context.strings.immediately; + } + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart new file mode 100644 index 0000000000..e6f8bc0ae2 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -0,0 +1,186 @@ +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/components/buttons/dynamic_fab.dart"; +import "package:ente_ui/components/buttons/icon_button_widget.dart"; +import "package:ente_ui/components/text_input_widget.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; + +class LockScreenConfirmPassword extends StatefulWidget { + const LockScreenConfirmPassword({ + super.key, + required this.password, + }); + final String password; + + @override + State createState() => + _LockScreenConfirmPasswordState(); +} + +class _LockScreenConfirmPasswordState extends State { + final _confirmPasswordController = TextEditingController(text: null); + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; + final _focusNode = FocusNode(); + final _isFormValid = ValueNotifier(false); + final _submitNotifier = ValueNotifier(false); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + _focusNode.requestFocus(); + }); + } + + @override + void dispose() { + _submitNotifier.dispose(); + _focusNode.dispose(); + _isFormValid.dispose(); + _confirmPasswordController.dispose(); + super.dispose(); + } + + Future _confirmPasswordMatch() async { + if (widget.password == _confirmPasswordController.text) { + await _lockscreenSetting.setPassword(_confirmPasswordController.text); + + Navigator.of(context).pop(true); + Navigator.of(context).pop(true); + return; + } + await HapticFeedback.vibrate(); + throw Exception("Incorrect password"); + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + final isKeypadOpen = MediaQuery.viewInsetsOf(context).bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + Navigator.of(context).pop(); + }, + icon: Icon( + Icons.arrow_back, + color: colorTheme.textBase, + ), + ), + ), + floatingActionButton: ValueListenableBuilder( + valueListenable: _isFormValid, + builder: (context, isFormValid, child) { + return DynamicFAB( + isKeypadOpen: isKeypadOpen, + buttonText: context.strings.confirm, + isFormValid: isFormValid, + onPressedFunction: () async { + _submitNotifier.value = !_submitNotifier.value; + }, + ); + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + body: SingleChildScrollView( + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 120, + width: 120, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 82, + height: 82, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Colors.grey.shade500.withOpacity(0.2), + Colors.grey.shade50.withOpacity(0.1), + Colors.grey.shade400.withOpacity(0.2), + Colors.grey.shade300.withOpacity(0.4), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorTheme.backgroundBase, + ), + ), + ), + ), + SizedBox( + height: 75, + width: 75, + child: CircularProgressIndicator( + color: colorTheme.fillFaintPressed, + value: 1, + strokeWidth: 1.5, + ), + ), + IconButtonWidget( + icon: Icons.lock, + iconButtonType: IconButtonType.primary, + iconColor: colorTheme.textBase, + ), + ], + ), + ), + Text( + context.strings.reEnterPassword, + style: textTheme.bodyBold, + ), + const Padding(padding: EdgeInsets.all(12)), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TextInputWidget( + hintText: context.strings.confirmPassword, + autoFocus: true, + textCapitalization: TextCapitalization.none, + isPasswordInput: true, + shouldSurfaceExecutionStates: false, + onChange: (p0) { + _confirmPasswordController.text = p0; + _isFormValid.value = + _confirmPasswordController.text.isNotEmpty; + }, + onSubmit: (p0) { + return _confirmPasswordMatch(); + }, + submitNotifier: _submitNotifier, + ), + ), + const Padding(padding: EdgeInsets.all(12)), + ], + ), + ), + ), + ); + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart new file mode 100644 index 0000000000..3f97324092 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -0,0 +1,212 @@ +import "dart:io"; + +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/custom_pin_keypad.dart"; +import "package:pinput/pinput.dart"; + +class LockScreenConfirmPin extends StatefulWidget { + const LockScreenConfirmPin({super.key, required this.pin}); + final String pin; + @override + State createState() => _LockScreenConfirmPinState(); +} + +class _LockScreenConfirmPinState extends State { + final _confirmPinController = TextEditingController(text: null); + bool isConfirmPinValid = false; + bool isPlatformDesktop = false; + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; + + @override + void initState() { + super.initState(); + isPlatformDesktop = + Platform.isLinux || Platform.isMacOS || Platform.isWindows; + } + + @override + void dispose() { + super.dispose(); + _confirmPinController.dispose(); + } + + Future _confirmPinMatch() async { + if (widget.pin == _confirmPinController.text) { + await _lockscreenSetting.setPin(_confirmPinController.text); + + Navigator.of(context).pop(true); + Navigator.of(context).pop(true); + return; + } + setState(() { + isConfirmPinValid = true; + }); + await HapticFeedback.vibrate(); + await Future.delayed(const Duration(milliseconds: 75)); + _confirmPinController.clear(); + setState(() { + isConfirmPinValid = false; + }); + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + return Scaffold( + appBar: AppBar( + elevation: 0, + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + icon: Icon( + Icons.arrow_back, + color: colorTheme.textBase, + ), + ), + ), + floatingActionButton: isPlatformDesktop + ? null + : CustomPinKeypad(controller: _confirmPinController), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + body: SingleChildScrollView( + child: _getBody(colorTheme, textTheme), + ), + ); + } + + Widget _getBody(colorTheme, textTheme) { + final pinPutDecoration = PinTheme( + height: 48, + width: 48, + padding: const EdgeInsets.only(top: 6.0), + decoration: BoxDecoration( + border: Border.all(color: colorTheme.primary500), + borderRadius: BorderRadius.circular(15.0), + ), + ); + + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 120, + width: 120, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 82, + height: 82, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Colors.grey.shade500.withOpacity(0.2), + Colors.grey.shade50.withOpacity(0.1), + Colors.grey.shade400.withOpacity(0.2), + Colors.grey.shade300.withOpacity(0.4), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorTheme.backgroundBase, + ), + ), + ), + ), + SizedBox( + height: 75, + width: 75, + child: ValueListenableBuilder( + valueListenable: _confirmPinController, + builder: (context, value, child) { + return TweenAnimationBuilder( + tween: Tween( + begin: 0, + end: _confirmPinController.text.length / 4, + ), + curve: Curves.ease, + duration: const Duration(milliseconds: 250), + builder: (context, value, _) => + CircularProgressIndicator( + backgroundColor: colorTheme.fillFaintPressed, + value: value, + color: colorTheme.primary400, + strokeWidth: 1.5, + ), + ); + }, + ), + ), + Icon( + Icons.lock, + color: colorTheme.textBase, + size: 30, + ), + ], + ), + ), + Text( + context.strings.reEnterPin, + style: textTheme.bodyBold, + ), + const Padding(padding: EdgeInsets.all(12)), + Pinput( + length: 4, + showCursor: false, + useNativeKeyboard: isPlatformDesktop, + autofocus: true, + controller: _confirmPinController, + defaultPinTheme: pinPutDecoration, + submittedPinTheme: pinPutDecoration.copyWith( + textStyle: textTheme.h3Bold, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.fillBase, + ), + ), + ), + followingPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.fillMuted, + ), + ), + ), + focusedPinTheme: pinPutDecoration, + errorPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.warning400, + ), + ), + ), + errorText: '', + obscureText: true, + obscuringCharacter: '*', + forceErrorState: isConfirmPinValid, + onCompleted: (value) async { + await _confirmPinMatch(); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart new file mode 100644 index 0000000000..190e4c971f --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -0,0 +1,399 @@ +import "dart:async"; +import "dart:io"; + +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/components/buttons/button_widget.dart"; +import "package:ente_ui/components/buttons/models/button_type.dart"; +import "package:ente_ui/components/captioned_text_widget.dart"; +import "package:ente_ui/components/dialog_widget.dart"; +import "package:ente_ui/components/divider_widget.dart"; +import "package:ente_ui/components/menu_item_widget.dart"; +import "package:ente_ui/components/title_bar_title_widget.dart"; +import "package:ente_ui/components/title_bar_widget.dart"; +import "package:ente_ui/components/toggle_switch_widget.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_utils/platform_util.dart"; +import "package:flutter/material.dart"; +import "package:lock_screen/local_authentication_service.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/app_lock.dart"; +import "package:lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:lock_screen/ui/lock_screen_password.dart"; +import "package:lock_screen/ui/lock_screen_pin.dart"; + +class LockScreenOptions extends StatefulWidget { + const LockScreenOptions({super.key}); + + @override + State createState() => _LockScreenOptionsState(); +} + +class _LockScreenOptionsState extends State { + final LockScreenSettings _lockScreenSettings = LockScreenSettings.instance; + late bool appLock = false; + bool isPinEnabled = false; + bool isPasswordEnabled = false; + late int autoLockTimeInMilliseconds; + late bool hideAppContent; + late bool isSystemLockEnabled = false; + + @override + void initState() { + super.initState(); + hideAppContent = _lockScreenSettings.getShouldHideAppContent(); + autoLockTimeInMilliseconds = _lockScreenSettings.getAutoLockTime(); + _initializeSettings(); + appLock = _lockScreenSettings.getIsAppLockSet(); + } + + Future _initializeSettings() async { + final bool passwordEnabled = await _lockScreenSettings.isPasswordSet(); + final bool pinEnabled = await _lockScreenSettings.isPinSet(); + final bool shouldHideAppContent = + _lockScreenSettings.getShouldHideAppContent(); + final bool systemLockEnabled = + _lockScreenSettings.shouldShowSystemLockScreen(); + setState(() { + isPasswordEnabled = passwordEnabled; + isPinEnabled = pinEnabled; + hideAppContent = shouldHideAppContent; + isSystemLockEnabled = systemLockEnabled; + }); + } + + Future _deviceLock() async { + if (await LocalAuthenticationService.instance + .isLocalAuthSupportedOnDevice()) { + await _lockScreenSettings.removePinAndPassword(); + await _lockScreenSettings.setSystemLockScreen(!isSystemLockEnabled); + } else { + await showDialogWidget( + context: context, + title: context.strings.noSystemLockFound, + body: context.strings.deviceLockEnablePreSteps, + isDismissible: true, + buttons: [ + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.ok, + isInAlert: true, + ), + ], + ); + } + await _initializeSettings(); + } + + Future _pinLock() async { + final bool result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return const LockScreenPin(); + }, + ), + ); + + if (result) { + await _lockScreenSettings.setSystemLockScreen(false); + await _lockScreenSettings.setAppLockEnabled(true); + setState(() { + appLock = _lockScreenSettings.getIsAppLockSet(); + }); + } + await _initializeSettings(); + } + + Future _passwordLock() async { + final bool result = await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return const LockScreenPassword(); + }, + ), + ); + if (result) { + await _lockScreenSettings.setSystemLockScreen(false); + setState(() { + appLock = _lockScreenSettings.getIsAppLockSet(); + }); + } + await _initializeSettings(); + } + + Future _onToggleSwitch() async { + AppLock.of(context)!.setEnabled(!appLock); + if (await LocalAuthenticationService.instance + .isLocalAuthSupportedOnDevice()) { + await _lockScreenSettings.setSystemLockScreen(!appLock); + await _lockScreenSettings.setAppLockEnabled(!appLock); + } else { + await _lockScreenSettings.setSystemLockScreen(false); + await _lockScreenSettings.setAppLockEnabled(false); + } + await _lockScreenSettings.removePinAndPassword(); + if (PlatformUtil.isMobile()) { + await _lockScreenSettings.setHideAppContent(!appLock); + setState(() { + hideAppContent = _lockScreenSettings.getShouldHideAppContent(); + }); + } + await _initializeSettings(); + setState(() { + appLock = !appLock; + }); + } + + Future _onAutoLock() async { + await routeToPage( + context, + const LockScreenAutoLock(), + ).then( + (value) { + setState(() { + autoLockTimeInMilliseconds = _lockScreenSettings.getAutoLockTime(); + }); + }, + ); + } + + Future _onHideContent() async { + setState(() { + hideAppContent = !hideAppContent; + }); + await _lockScreenSettings.setHideAppContent(hideAppContent); + } + + String _formatTime(Duration duration) { + if (duration.inHours != 0) { + return "in ${duration.inHours} hour${duration.inHours > 1 ? 's' : ''}"; + } else if (duration.inMinutes != 0) { + return "in ${duration.inMinutes} minute${duration.inMinutes > 1 ? 's' : ''}"; + } else if (duration.inSeconds != 0) { + return "in ${duration.inSeconds} second${duration.inSeconds > 1 ? 's' : ''}"; + } else { + return context.strings.immediately; + } + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + return Scaffold( + body: CustomScrollView( + primary: false, + slivers: [ + TitleBarWidget( + flexibleSpaceTitle: TitleBarTitleWidget( + title: context.strings.appLock, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: context.strings.appLock, + ), + alignCaptionedTextToLeft: true, + singleBorderRadius: 8, + menuItemColor: colorTheme.fillFaint, + trailingWidget: ToggleSwitchWidget( + value: () => appLock, + onChanged: () => _onToggleSwitch(), + ), + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 210), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: !appLock + ? Padding( + padding: const EdgeInsets.only( + top: 14, + left: 14, + right: 12, + ), + child: Text( + context.strings.appLockDescription, + style: textTheme.miniFaint, + textAlign: TextAlign.left, + ), + ) + : const SizedBox(), + ), + const Padding( + padding: EdgeInsets.only(top: 24), + ), + ], + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 210), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: appLock + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: context.strings.deviceLock, + ), + surfaceExecutionStates: false, + alignCaptionedTextToLeft: true, + isTopBorderRadiusRemoved: false, + isBottomBorderRadiusRemoved: true, + menuItemColor: colorTheme.fillFaint, + trailingIcon: isSystemLockEnabled + ? Icons.check + : null, + trailingIconColor: colorTheme.textBase, + onTap: () => _deviceLock(), + ), + DividerWidget( + dividerType: DividerType.menuNoIcon, + bgColor: colorTheme.fillFaint, + ), + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: context.strings.pinLock, + ), + surfaceExecutionStates: false, + alignCaptionedTextToLeft: true, + isTopBorderRadiusRemoved: true, + isBottomBorderRadiusRemoved: true, + menuItemColor: colorTheme.fillFaint, + trailingIcon: + isPinEnabled ? Icons.check : null, + trailingIconColor: colorTheme.textBase, + onTap: () => _pinLock(), + ), + DividerWidget( + dividerType: DividerType.menuNoIcon, + bgColor: colorTheme.fillFaint, + ), + MenuItemWidget( + captionedTextWidget: CaptionedTextWidget( + title: context.strings.password, + ), + surfaceExecutionStates: false, + alignCaptionedTextToLeft: true, + isTopBorderRadiusRemoved: true, + isBottomBorderRadiusRemoved: false, + menuItemColor: colorTheme.fillFaint, + trailingIcon: isPasswordEnabled + ? Icons.check + : null, + trailingIconColor: colorTheme.textBase, + onTap: () => _passwordLock(), + ), + const SizedBox( + height: 24, + ), + PlatformUtil.isMobile() + ? MenuItemWidget( + captionedTextWidget: + CaptionedTextWidget( + title: context.strings.autoLock, + subTitle: _formatTime( + Duration( + milliseconds: + autoLockTimeInMilliseconds, + ), + ), + ), + surfaceExecutionStates: false, + alignCaptionedTextToLeft: true, + singleBorderRadius: 8, + menuItemColor: colorTheme.fillFaint, + trailingIconColor: + colorTheme.textBase, + onTap: () => _onAutoLock(), + ) + : const SizedBox.shrink(), + PlatformUtil.isMobile() + ? Padding( + padding: const EdgeInsets.only( + top: 14, + left: 14, + right: 12, + ), + child: Text( + context.strings + .autoLockFeatureDescription, + style: textTheme.miniFaint, + textAlign: TextAlign.left, + ), + ) + : const SizedBox.shrink(), + PlatformUtil.isMobile() + ? Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + const SizedBox(height: 24), + MenuItemWidget( + captionedTextWidget: + CaptionedTextWidget( + title: context + .strings.hideContent, + ), + alignCaptionedTextToLeft: true, + singleBorderRadius: 8, + menuItemColor: + colorTheme.fillFaint, + trailingWidget: + ToggleSwitchWidget( + value: () => hideAppContent, + onChanged: () => + _onHideContent(), + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 14, + left: 14, + right: 12, + ), + child: Text( + Platform.isAndroid + ? context.strings + .hideContentDescriptionAndroid + : context.strings + .hideContentDescriptioniOS, + style: textTheme.miniFaint, + textAlign: TextAlign.left, + ), + ), + ], + ) + : const SizedBox.shrink(), + ], + ) + : const SizedBox.shrink(), + ), + ], + ), + ), + ); + }, + childCount: 1, + ), + ), + ], + ), + ); + } + + routeToPage(BuildContext context, LockScreenAutoLock lockScreenAutoLock) {} +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart new file mode 100644 index 0000000000..a010a327e6 --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -0,0 +1,250 @@ +import "dart:convert"; + +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/components/buttons/dynamic_fab.dart"; +import "package:ente_ui/components/buttons/icon_button_widget.dart"; +import "package:ente_ui/components/text_input_widget.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:lock_screen/ui/lock_screen_options.dart"; + +/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. +/// Set to true when the app requires the user to authenticate before allowing +/// changes to the lock screen settings. + +/// [isAuthenticatingOnAppLaunch] Authentication required on app launch. +/// Set to true when the app requires the user to authenticate immediately upon opening. + +/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password). +/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes. + +class LockScreenPassword extends StatefulWidget { + const LockScreenPassword({ + super.key, + this.isChangingLockScreenSettings = false, + this.isAuthenticatingOnAppLaunch = false, + this.isAuthenticatingForInAppChange = false, + this.authPass, + }); + + final bool isChangingLockScreenSettings; + final bool isAuthenticatingOnAppLaunch; + final bool isAuthenticatingForInAppChange; + final String? authPass; + @override + State createState() => _LockScreenPasswordState(); +} + +class _LockScreenPasswordState extends State { + final _passwordController = TextEditingController(text: null); + final _focusNode = FocusNode(); + final _isFormValid = ValueNotifier(false); + final _submitNotifier = ValueNotifier(false); + int invalidAttemptsCount = 0; + + final _lockscreenSetting = LockScreenSettings.instance; + @override + void initState() { + super.initState(); + invalidAttemptsCount = _lockscreenSetting.getInvalidAttemptCount(); + WidgetsBinding.instance.addPostFrameCallback((_) async { + _focusNode.requestFocus(); + }); + } + + @override + void dispose() { + super.dispose(); + _submitNotifier.dispose(); + _focusNode.dispose(); + _isFormValid.dispose(); + _passwordController.dispose(); + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + final isKeypadOpen = MediaQuery.viewInsetsOf(context).bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + Navigator.of(context).pop(false); + }, + icon: Icon( + Icons.arrow_back, + color: colorTheme.textBase, + ), + ), + ), + floatingActionButton: ValueListenableBuilder( + valueListenable: _isFormValid, + builder: (context, isFormValid, child) { + return DynamicFAB( + isKeypadOpen: isKeypadOpen, + buttonText: context.strings.next, + isFormValid: isFormValid, + onPressedFunction: () async { + _submitNotifier.value = !_submitNotifier.value; + }, + ); + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + body: SingleChildScrollView( + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 120, + width: 120, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 82, + height: 82, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Colors.grey.shade500.withOpacity(0.2), + Colors.grey.shade50.withOpacity(0.1), + Colors.grey.shade400.withOpacity(0.2), + Colors.grey.shade300.withOpacity(0.4), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorTheme.backgroundBase, + ), + ), + ), + ), + SizedBox( + height: 75, + width: 75, + child: CircularProgressIndicator( + color: colorTheme.fillFaintPressed, + value: 1, + strokeWidth: 1.5, + ), + ), + IconButtonWidget( + icon: Icons.lock, + iconButtonType: IconButtonType.primary, + iconColor: colorTheme.textBase, + ), + ], + ), + ), + Text( + widget.isChangingLockScreenSettings + ? context.strings.enterPassword + : context.strings.setNewPassword, + textAlign: TextAlign.center, + style: textTheme.bodyBold, + ), + const Padding(padding: EdgeInsets.all(12)), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: TextInputWidget( + hintText: context.strings.password, + autoFocus: true, + textCapitalization: TextCapitalization.none, + isPasswordInput: true, + shouldSurfaceExecutionStates: false, + onChange: (p0) { + _passwordController.text = p0; + _isFormValid.value = _passwordController.text.isNotEmpty; + }, + onSubmit: (p0) { + return _confirmPassword(); + }, + submitNotifier: _submitNotifier, + ), + ), + const Padding(padding: EdgeInsets.all(12)), + ], + ), + ), + ), + ); + } + + Future _confirmPasswordAuth(String inputtedPassword) async { + final Uint8List? salt = await _lockscreenSetting.getSalt(); + final hash = cryptoPwHash( + utf8.encode(inputtedPassword), + salt!, + sodium.crypto.pwhash.memLimitInteractive, + sodium.crypto.pwhash.opsLimitSensitive, + sodium, + ); + if (widget.authPass == base64Encode(hash)) { + await _lockscreenSetting.setInvalidAttemptCount(0); + + widget.isAuthenticatingOnAppLaunch || + widget.isAuthenticatingForInAppChange + ? Navigator.of(context).pop(true) + : Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => const LockScreenOptions(), + ), + ); + return true; + } else { + if (widget.isAuthenticatingOnAppLaunch) { + invalidAttemptsCount++; + await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); + if (invalidAttemptsCount > 4) { + Navigator.of(context).pop(false); + } + } + + await HapticFeedback.vibrate(); + throw Exception("Incorrect password"); + } + } + + Future _confirmPassword() async { + if (widget.isChangingLockScreenSettings) { + await _confirmPasswordAuth(_passwordController.text); + return; + } else { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => LockScreenConfirmPassword( + password: _passwordController.text, + ), + ), + ); + _passwordController.clear(); + } + } +} diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart new file mode 100644 index 0000000000..6a2b6a858b --- /dev/null +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -0,0 +1,285 @@ +import "dart:convert"; +import "dart:io"; + +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/theme/colors.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_ui/theme/text_style.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/custom_pin_keypad.dart"; +import "package:lock_screen/ui/lock_screen_confirm_pin.dart"; +import "package:lock_screen/ui/lock_screen_options.dart"; +import 'package:pinput/pinput.dart'; + +/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. +/// Set to true when the app requires the user to authenticate before allowing +/// changes to the lock screen settings. + +/// [isAuthenticatingOnAppLaunch] Authentication required on app launch. +/// Set to true when the app requires the user to authenticate immediately upon opening. + +/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password). +/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes. + +class LockScreenPin extends StatefulWidget { + const LockScreenPin({ + super.key, + this.isChangingLockScreenSettings = false, + this.isAuthenticatingOnAppLaunch = false, + this.isAuthenticatingForInAppChange = false, + this.authPin, + }); + + final bool isAuthenticatingOnAppLaunch; + final bool isChangingLockScreenSettings; + final bool isAuthenticatingForInAppChange; + final String? authPin; + @override + State createState() => _LockScreenPinState(); +} + +class _LockScreenPinState extends State { + final _pinController = TextEditingController(text: null); + + final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; + bool isPinValid = false; + int invalidAttemptsCount = 0; + bool isPlatformDesktop = false; + @override + void initState() { + super.initState(); + isPlatformDesktop = + Platform.isLinux || Platform.isMacOS || Platform.isWindows; + invalidAttemptsCount = _lockscreenSetting.getInvalidAttemptCount(); + } + + @override + void dispose() { + super.dispose(); + _pinController.dispose(); + } + + Future confirmPinAuth(String inputtedPin) async { + final Uint8List? salt = await _lockscreenSetting.getSalt(); + final hash = cryptoPwHash( + utf8.encode(inputtedPin), + salt!, + sodium.crypto.pwhash.memLimitInteractive, + sodium.crypto.pwhash.opsLimitSensitive, + sodium, + ); + if (widget.authPin == base64Encode(hash)) { + invalidAttemptsCount = 0; + await _lockscreenSetting.setInvalidAttemptCount(0); + widget.isAuthenticatingOnAppLaunch || + widget.isAuthenticatingForInAppChange + ? Navigator.of(context).pop(true) + : Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (context) => const LockScreenOptions(), + ), + ); + return true; + } else { + setState(() { + isPinValid = true; + }); + await HapticFeedback.vibrate(); + await Future.delayed(const Duration(milliseconds: 75)); + _pinController.clear(); + setState(() { + isPinValid = false; + }); + + if (widget.isAuthenticatingOnAppLaunch) { + invalidAttemptsCount++; + await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); + if (invalidAttemptsCount > 4) { + Navigator.of(context).pop(false); + } + } + return false; + } + } + + Future _confirmPin(String inputtedPin) async { + if (widget.isChangingLockScreenSettings) { + await confirmPinAuth(inputtedPin); + return; + } else { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => + LockScreenConfirmPin(pin: inputtedPin), + ), + ); + _pinController.clear(); + } + } + + @override + Widget build(BuildContext context) { + final colorTheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + final pinPutDecoration = PinTheme( + height: 48, + width: 48, + padding: const EdgeInsets.only(top: 6.0), + decoration: BoxDecoration( + border: Border.all(color: colorTheme.primary500), + borderRadius: BorderRadius.circular(15.0), + ), + ); + return Scaffold( + appBar: AppBar( + elevation: 0, + leading: IconButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + icon: Icon( + Icons.arrow_back, + color: colorTheme.textBase, + ), + ), + ), + floatingActionButton: isPlatformDesktop + ? null + : CustomPinKeypad(controller: _pinController), + floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, + body: SingleChildScrollView( + child: _getBody(colorTheme, textTheme, pinPutDecoration), + ), + ); + } + + Widget _getBody( + EnteColorScheme colorTheme, + EnteTextTheme textTheme, + PinTheme pinPutDecoration, + ) { + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 120, + width: 120, + child: Stack( + alignment: Alignment.center, + children: [ + Container( + width: 82, + height: 82, + decoration: BoxDecoration( + shape: BoxShape.circle, + gradient: LinearGradient( + colors: [ + Colors.grey.shade500.withOpacity(0.2), + Colors.grey.shade50.withOpacity(0.1), + Colors.grey.shade400.withOpacity(0.2), + Colors.grey.shade300.withOpacity(0.4), + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: Padding( + padding: const EdgeInsets.all(1.0), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: colorTheme.backgroundBase, + ), + ), + ), + ), + SizedBox( + height: 75, + width: 75, + child: ValueListenableBuilder( + valueListenable: _pinController, + builder: (context, value, child) { + return TweenAnimationBuilder( + tween: Tween( + begin: 0, + end: _pinController.text.length / 4, + ), + curve: Curves.ease, + duration: const Duration(milliseconds: 250), + builder: (context, value, _) => + CircularProgressIndicator( + backgroundColor: colorTheme.fillFaintPressed, + value: value, + color: colorTheme.primary400, + strokeWidth: 1.5, + ), + ); + }, + ), + ), + Icon( + Icons.lock, + color: colorTheme.textBase, + size: 30, + ), + ], + ), + ), + Text( + widget.isChangingLockScreenSettings + ? context.strings.enterPin + : context.strings.setNewPin, + style: textTheme.bodyBold, + ), + const Padding(padding: EdgeInsets.all(12)), + Pinput( + length: 4, + showCursor: false, + useNativeKeyboard: isPlatformDesktop, + controller: _pinController, + autofocus: true, + defaultPinTheme: pinPutDecoration, + submittedPinTheme: pinPutDecoration.copyWith( + textStyle: textTheme.h3Bold, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.fillBase, + ), + ), + ), + followingPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.fillMuted, + ), + ), + ), + focusedPinTheme: pinPutDecoration, + errorPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorTheme.warning400, + ), + ), + ), + forceErrorState: isPinValid, + obscureText: true, + obscuringCharacter: '*', + errorText: '', + onCompleted: (value) async { + await _confirmPin(_pinController.text); + }, + ), + ], + ), + ); + } +} diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock new file mode 100644 index 0000000000..29bd04fca3 --- /dev/null +++ b/mobile/packages/lock_screen/pubspec.lock @@ -0,0 +1,1279 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + app_links: + dependency: transitive + description: + name: app_links + sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: transitive + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: df26af0de7c4eff46c53c190b5590e22457bfce6ea679aedb1e6326197f27d6f + url: "https://pub.dev" + source: hosted + version: "1.4.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: transitive + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + dotted_border: + dependency: transitive + description: + name: dotted_border + sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + email_validator: + dependency: transitive + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_accounts: + dependency: "direct main" + description: + path: "../accounts" + relative: true + source: path + version: "1.0.0" + ente_base: + dependency: transitive + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: transitive + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + ente_network: + dependency: transitive + description: + path: "../network" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: "direct main" + description: + path: "../ui" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: "direct main" + description: + path: "../utils" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_saver: + dependency: transitive + description: + name: file_saver + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: "direct main" + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_email_sender: + dependency: transitive + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_local_authentication: + dependency: "direct main" + description: + name: flutter_local_authentication + sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" + flutter_secure_storage: + dependency: "direct main" + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + jni: + dependency: transitive + description: + name: jni + sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 + url: "https://pub.dev" + source: hosted + version: "0.14.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + local_auth: + dependency: "direct main" + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + url: "https://pub.dev" + source: hosted + version: "1.0.50" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: transitive + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_dio_adapter: + dependency: transitive + description: + name: native_dio_adapter + sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + password_strength: + dependency: transitive + description: + name: password_strength + sha256: "0e51e3d864e37873a1347e658147f88b66e141ee36c58e19828dc5637961e1ce" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + privacy_screen: + dependency: "direct main" + description: + name: privacy_screen + sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: transitive + description: + name: share_plus + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + step_progress_indicator: + dependency: transitive + description: + name: step_progress_indicator + sha256: b51bb1fcfc78454359f0658c5a2c21548c3825ebf76e826308e9ca10f383bbb8 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + styled_text: + dependency: transitive + description: + name: styled_text + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 + url: "https://pub.dev" + source: hosted + version: "8.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + ua_client_hints: + dependency: transitive + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xmlstream: + dependency: transitive + description: + name: xmlstream + sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml new file mode 100644 index 0000000000..cd611a61f4 --- /dev/null +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -0,0 +1,42 @@ +name: lock_screen +description: A Flutter package containing lock screen UI components and services for Ente apps +version: 1.0.0 +publish_to: none + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + ente_accounts: + path: ../accounts + ente_configuration: + path: ../configuration + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../events + ente_strings: + path: ../strings + ente_ui: + path: ../ui + ente_utils: + path: ../utils + flutter_animate: ^4.1.0 + flutter_local_authentication: ^1.1.11 + flutter_secure_storage: ^9.0.0 + local_auth: ^2.1.8 + logging: ^1.1.1 + pinput: ^5.0.0 + privacy_screen: ^0.0.8 + shared_preferences: ^2.5.3 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: From c09922d1a306cd4d55dc2f68df7f431936d5d9f7 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:08:52 +0530 Subject: [PATCH 032/164] Update strings --- .../strings/lib/l10n/arb/strings_en.arb | 71 ++++++++++++ .../lib/l10n/strings_localizations.dart | 108 ++++++++++++++++++ .../lib/l10n/strings_localizations_ar.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_bg.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_cs.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_da.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_el.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_en.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_es.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_fr.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_id.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_ja.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_ko.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_lt.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_nl.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_pl.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_pt.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_ru.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_sk.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_sr.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_sv.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_tr.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_vi.dart | 62 ++++++++++ .../lib/l10n/strings_localizations_zh.dart | 62 ++++++++++ 24 files changed, 1543 insertions(+) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 980994e3c3..aa8dfc0ae5 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -703,5 +703,76 @@ "iOSOkButton": "OK", "@iOSOkButton": { "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Email already registered.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Email not registered.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "This email is already in use", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Email changed to {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Authentication failed, please try again", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Authentication successful!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Session expired", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Incorrect recovery key", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "The recovery key you entered is incorrect", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Two-factor authentication successfully reset", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "noRecoveryKey": "No recovery key", + "@noRecoveryKey": { + "description": "Error message when no recovery key is found" + }, + "yourAccountHasBeenDeleted": "Your account has been deleted", + "@yourAccountHasBeenDeleted": { + "description": "Confirmation message when account has been deleted" + }, + "verificationId": "Verification ID", + "@verificationId": { + "description": "Label for verification ID" + }, + "yourVerificationCodeHasExpired": "Your verification code has expired", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Incorrect code", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, the code you've entered is incorrect", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index ccb3bf5cc9..56f153543e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -1153,6 +1153,114 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'OK'** String get iOSOkButton; + + /// Error message when email is already registered + /// + /// In en, this message translates to: + /// **'Email already registered.'** + String get emailAlreadyRegistered; + + /// Error message when email is not registered + /// + /// In en, this message translates to: + /// **'Email not registered.'** + String get emailNotRegistered; + + /// Error message when email is already in use + /// + /// In en, this message translates to: + /// **'This email is already in use'** + String get thisEmailIsAlreadyInUse; + + /// Message when email has been changed + /// + /// In en, this message translates to: + /// **'Email changed to {newEmail}'** + String emailChangedTo(String newEmail); + + /// Error message when authentication fails + /// + /// In en, this message translates to: + /// **'Authentication failed, please try again'** + String get authenticationFailedPleaseTryAgain; + + /// Success message when authentication is successful + /// + /// In en, this message translates to: + /// **'Authentication successful!'** + String get authenticationSuccessful; + + /// Error message when session has expired + /// + /// In en, this message translates to: + /// **'Session expired'** + String get sessionExpired; + + /// Error message when recovery key is incorrect + /// + /// In en, this message translates to: + /// **'Incorrect recovery key'** + String get incorrectRecoveryKey; + + /// Detailed error message when recovery key is incorrect + /// + /// In en, this message translates to: + /// **'The recovery key you entered is incorrect'** + String get theRecoveryKeyYouEnteredIsIncorrect; + + /// Message when two-factor authentication is successfully reset + /// + /// In en, this message translates to: + /// **'Two-factor authentication successfully reset'** + String get twofactorAuthenticationSuccessfullyReset; + + /// Error message when no recovery key is found + /// + /// In en, this message translates to: + /// **'No recovery key'** + String get noRecoveryKey; + + /// Status message while waiting for WiFi connection + /// + /// In en, this message translates to: + /// **'Waiting for WiFi...'** + String get waitingForWifi; + + /// Error message when attempting to delete the default account + /// + /// In en, this message translates to: + /// **'You cannot delete your default account'** + String get youCannotDeleteYourDefaultAccount; + + /// Confirmation message when account has been deleted + /// + /// In en, this message translates to: + /// **'Your account has been deleted'** + String get yourAccountHasBeenDeleted; + + /// Label for verification ID + /// + /// In en, this message translates to: + /// **'Verification ID'** + String get verificationId; + + /// Error message when verification code has expired + /// + /// In en, this message translates to: + /// **'Your verification code has expired'** + String get yourVerificationCodeHasExpired; + + /// Error message when code is incorrect + /// + /// In en, this message translates to: + /// **'Incorrect code'** + String get incorrectCode; + + /// Detailed error message when code is incorrect + /// + /// In en, this message translates to: + /// **'Sorry, the code you\'ve entered is incorrect'** + String get sorryTheCodeYouveEnteredIsIncorrect; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 98bdc172d3..adfe676f5d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -564,4 +564,66 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 60dce5783c..2586324842 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -564,4 +564,66 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index d459600125..dbce15788d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -564,4 +564,66 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index a458398a05..d7f0abc3df 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -564,4 +564,66 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index cf2f113dd8..87ced4197d 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -564,4 +564,66 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 20488e207b..01f51c2561 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -564,4 +564,66 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 5afce95fa1..6c1e0aaecf 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -564,4 +564,66 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 25b22d66c6..4109cf4fba 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -564,4 +564,66 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 3cd8b4b05a..16546527ba 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -564,4 +564,66 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 6c9e63b325..56a398d9d3 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -564,4 +564,66 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index 9fa75484b8..b144ae51cf 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -564,4 +564,66 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 702d0ab86b..036dd81fec 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -564,4 +564,66 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 94d1c252cb..6a95f9f7d5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -564,4 +564,66 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index c4a477f29e..c72f89835c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -564,4 +564,66 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index d3db542580..b848a94a68 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -564,4 +564,66 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index b944b53c3f..753036325e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -564,4 +564,66 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 99f32a6738..95c8373d02 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -564,4 +564,66 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 079750b0a6..63ddbf5858 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -564,4 +564,66 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 65f7eed158..81aedc19a1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -564,4 +564,66 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index c3c6004baa..0f95b247d8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -564,4 +564,66 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 7ba7281dac..742890b81a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -564,4 +564,66 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index ea01a6f2ce..51775fe459 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -563,6 +563,68 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get waitingForWifi => 'Waiting for WiFi...'; + + @override + String get youCannotDeleteYourDefaultAccount => + 'You cannot delete your default account'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From 045b40b2b2eb41a28e2eac0a6c0af4f516ee5dad Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:01 +0530 Subject: [PATCH 033/164] Update common lockscreen --- mobile/packages/lock_screen/lib/auth_util.dart | 4 ++-- .../lib/local_authentication_service.dart | 10 +++++----- mobile/packages/lock_screen/lib/ui/app_lock.dart | 2 +- mobile/packages/lock_screen/lib/ui/lock_screen.dart | 6 +++--- .../lock_screen/lib/ui/lock_screen_auto_lock.dart | 2 +- .../lib/ui/lock_screen_confirm_password.dart | 2 +- .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 4 ++-- .../lock_screen/lib/ui/lock_screen_options.dart | 12 ++++++------ .../lock_screen/lib/ui/lock_screen_password.dart | 6 +++--- .../packages/lock_screen/lib/ui/lock_screen_pin.dart | 8 ++++---- mobile/packages/lock_screen/pubspec.yaml | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index e1574ab8db..396be4f809 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -6,8 +6,8 @@ import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:lock_screen/local_authentication_service.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index 2ff4b0b673..6ae795c61e 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -7,11 +7,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:lock_screen/auth_util.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; -import 'package:lock_screen/ui/app_lock.dart'; -import 'package:lock_screen/ui/lock_screen_password.dart'; -import 'package:lock_screen/ui/lock_screen_pin.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen_password.dart'; +import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index 4e93ce5ec1..f72fa1e8a5 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index 74cbcfc554..e89869b740 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -9,9 +9,9 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:lock_screen/auth_util.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; -import 'package:lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index dfdc11b8f5..1f0901ed50 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -7,7 +7,7 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index e6f8bc0ae2..716a1ad7da 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -5,7 +5,7 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index 3f97324092..33ce921012 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -4,8 +4,8 @@ import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/custom_pin_keypad.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 190e4c971f..2bf9d21131 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -14,12 +14,12 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; -import "package:lock_screen/local_authentication_service.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/app_lock.dart"; -import "package:lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:lock_screen/ui/lock_screen_password.dart"; -import "package:lock_screen/ui/lock_screen_pin.dart"; +import "package:ente_lock_screen/local_authentication_service.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/app_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index a010a327e6..93a1c4d636 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -8,9 +8,9 @@ import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:lock_screen/ui/lock_screen_options.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 6a2b6a858b..2001cae468 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -8,10 +8,10 @@ import "package:ente_ui/theme/text_style.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/custom_pin_keypad.dart"; -import "package:lock_screen/ui/lock_screen_confirm_pin.dart"; -import "package:lock_screen/ui/lock_screen_options.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index cd611a61f4..02b553a96f 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -1,4 +1,4 @@ -name: lock_screen +name: ente_lock_screen description: A Flutter package containing lock screen UI components and services for Ente apps version: 1.0.0 publish_to: none From 3436fb7fb15faf6483aba63c103cc2ee4f9733ad Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:09 +0530 Subject: [PATCH 034/164] Update common ui --- mobile/packages/ui/pubspec.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index a25bb42857..56bcf90e1f 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -145,7 +145,7 @@ packages: source: path version: "1.0.0" ente_configuration: - dependency: transitive + dependency: "direct main" description: path: "../configuration" relative: true @@ -224,10 +224,10 @@ packages: dependency: transitive description: name: file_saver - sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.2.14" + version: "0.3.1" fixnum: dependency: transitive description: @@ -700,18 +700,18 @@ packages: dependency: transitive description: name: share_plus - sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 url: "https://pub.dev" source: hosted - version: "10.1.4" + version: "11.0.0" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" url: "https://pub.dev" source: hosted - version: "5.0.2" + version: "6.0.0" shared_preferences: dependency: "direct main" description: From 21b3bdf2041795619dd44a0a0c3bd7ef2546c8d2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 035/164] Setup common accounts package --- .../packages/accounts/analysis_options.yaml | 1 + .../packages/accounts/lib/ente_accounts.dart | 33 + .../packages/accounts/lib/models/bonus.dart | 60 + .../accounts/lib/models/delete_account.dart | 9 + .../packages/accounts/lib/models/errors.dart | 7 + .../accounts/lib/models/sessions.dart | 45 + .../accounts/lib/models/set_keys_request.dart | 25 + .../lib/models/set_recovery_key_request.dart | 22 + mobile/packages/accounts/lib/models/srp.dart | 136 ++ .../accounts/lib/models/subscription.dart | 117 ++ .../accounts/lib/models/two_factor.dart | 11 + .../accounts/lib/models/user_details.dart | 249 ++++ .../lib/pages/change_email_dialog.dart | 81 ++ .../lib/pages/delete_account_page.dart | 251 ++++ .../accounts/lib/pages/email_entry_page.dart | 509 +++++++ .../accounts/lib/pages/login_page.dart | 221 +++ .../pages/login_pwd_verification_page.dart | 349 +++++ .../lib/pages/ott_verification_page.dart | 221 +++ .../accounts/lib/pages/passkey_page.dart | 234 +++ .../lib/pages/password_entry_page.dart | 520 +++++++ .../lib/pages/password_reentry_page.dart | 332 +++++ .../accounts/lib/pages/recovery_key_page.dart | 364 +++++ .../accounts/lib/pages/recovery_page.dart | 167 +++ .../pages/request_pwd_verification_page.dart | 222 +++ .../accounts/lib/pages/sessions_page.dart | 231 +++ .../pages/two_factor_authentication_page.dart | 163 +++ .../lib/pages/two_factor_recovery_page.dart | 110 ++ .../lib/services/passkey_service.dart | 57 + .../accounts/lib/services/user_service.dart | 1091 ++++++++++++++ mobile/packages/accounts/pubspec.lock | 1279 +++++++++++++++++ mobile/packages/accounts/pubspec.yaml | 62 + 31 files changed, 7179 insertions(+) create mode 100644 mobile/packages/accounts/analysis_options.yaml create mode 100644 mobile/packages/accounts/lib/ente_accounts.dart create mode 100644 mobile/packages/accounts/lib/models/bonus.dart create mode 100644 mobile/packages/accounts/lib/models/delete_account.dart create mode 100644 mobile/packages/accounts/lib/models/errors.dart create mode 100644 mobile/packages/accounts/lib/models/sessions.dart create mode 100644 mobile/packages/accounts/lib/models/set_keys_request.dart create mode 100644 mobile/packages/accounts/lib/models/set_recovery_key_request.dart create mode 100644 mobile/packages/accounts/lib/models/srp.dart create mode 100644 mobile/packages/accounts/lib/models/subscription.dart create mode 100644 mobile/packages/accounts/lib/models/two_factor.dart create mode 100644 mobile/packages/accounts/lib/models/user_details.dart create mode 100644 mobile/packages/accounts/lib/pages/change_email_dialog.dart create mode 100644 mobile/packages/accounts/lib/pages/delete_account_page.dart create mode 100644 mobile/packages/accounts/lib/pages/email_entry_page.dart create mode 100644 mobile/packages/accounts/lib/pages/login_page.dart create mode 100644 mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart create mode 100644 mobile/packages/accounts/lib/pages/ott_verification_page.dart create mode 100644 mobile/packages/accounts/lib/pages/passkey_page.dart create mode 100644 mobile/packages/accounts/lib/pages/password_entry_page.dart create mode 100644 mobile/packages/accounts/lib/pages/password_reentry_page.dart create mode 100644 mobile/packages/accounts/lib/pages/recovery_key_page.dart create mode 100644 mobile/packages/accounts/lib/pages/recovery_page.dart create mode 100644 mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart create mode 100644 mobile/packages/accounts/lib/pages/sessions_page.dart create mode 100644 mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart create mode 100644 mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart create mode 100644 mobile/packages/accounts/lib/services/passkey_service.dart create mode 100644 mobile/packages/accounts/lib/services/user_service.dart create mode 100644 mobile/packages/accounts/pubspec.lock create mode 100644 mobile/packages/accounts/pubspec.yaml diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml new file mode 100644 index 0000000000..f9b303465f --- /dev/null +++ b/mobile/packages/accounts/analysis_options.yaml @@ -0,0 +1 @@ +include: package:flutter_lints/flutter.yaml diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart new file mode 100644 index 0000000000..e7865b2f1b --- /dev/null +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -0,0 +1,33 @@ +/// A Flutter package containing account-related functionality for Ente apps +library ente_accounts; + +// Models +export 'models/user_details.dart'; +export 'models/sessions.dart'; +export 'models/two_factor.dart'; +export 'models/srp.dart'; +export 'models/delete_account.dart'; +export 'models/set_keys_request.dart'; +export 'models/set_recovery_key_request.dart'; +export 'models/bonus.dart'; +export 'models/subscription.dart'; + +// Services +export 'services/user_service.dart'; + +// Pages +export 'pages/change_email_dialog.dart'; +export 'pages/delete_account_page.dart'; +export 'pages/email_entry_page.dart'; +export 'pages/login_page.dart'; +export 'pages/login_pwd_verification_page.dart'; +export 'pages/ott_verification_page.dart'; +export 'pages/passkey_page.dart'; +export 'pages/password_entry_page.dart'; +export 'pages/password_reentry_page.dart'; +export 'pages/recovery_key_page.dart'; +export 'pages/recovery_page.dart'; +export 'pages/request_pwd_verification_page.dart'; +export 'pages/sessions_page.dart'; +export 'pages/two_factor_authentication_page.dart'; +export 'pages/two_factor_recovery_page.dart'; diff --git a/mobile/packages/accounts/lib/models/bonus.dart b/mobile/packages/accounts/lib/models/bonus.dart new file mode 100644 index 0000000000..a41ea62499 --- /dev/null +++ b/mobile/packages/accounts/lib/models/bonus.dart @@ -0,0 +1,60 @@ +class Bonus { + int storage; + String type; + int validTill; + bool isRevoked; + + Bonus(this.storage, this.type, this.validTill, this.isRevoked); + + factory Bonus.fromJson(Map json) { + return Bonus( + json['storage'], + json['type'], + json['validTill'], + json['isRevoked'], + ); + } + + Map toJson() { + return { + 'storage': storage, + 'type': type, + 'validTill': validTill, + 'isRevoked': isRevoked, + }; + } +} + +class BonusData { + static Set signUpBonusTypes = {'SIGN_UP', 'REFERRAL'}; + final List storageBonuses; + + BonusData(this.storageBonuses); + + List getAddOnBonuses() { + return storageBonuses + .where((b) => !signUpBonusTypes.contains(b.type)) + .toList(); + } + + int totalAddOnBonus() { + return getAddOnBonuses().fold(0, (sum, bonus) => sum + bonus.storage); + } + + factory BonusData.fromJson(Map? json) { + if (json == null || json['storageBonuses'] == null) { + return BonusData([]); + } + return BonusData( + (json['storageBonuses'] as List) + .map((bonus) => Bonus.fromJson(bonus)) + .toList(), + ); + } + + Map toJson() { + return { + 'storageBonuses': storageBonuses.map((bonus) => bonus.toJson()).toList(), + }; + } +} diff --git a/mobile/packages/accounts/lib/models/delete_account.dart b/mobile/packages/accounts/lib/models/delete_account.dart new file mode 100644 index 0000000000..71ca04e9c3 --- /dev/null +++ b/mobile/packages/accounts/lib/models/delete_account.dart @@ -0,0 +1,9 @@ +class DeleteChallengeResponse { + final bool allowDelete; + final String encryptedChallenge; + + DeleteChallengeResponse({ + required this.allowDelete, + required this.encryptedChallenge, + }); +} diff --git a/mobile/packages/accounts/lib/models/errors.dart b/mobile/packages/accounts/lib/models/errors.dart new file mode 100644 index 0000000000..dab8c14e1e --- /dev/null +++ b/mobile/packages/accounts/lib/models/errors.dart @@ -0,0 +1,7 @@ +class UnauthorizedError extends Error {} + +class PassKeySessionNotVerifiedError extends Error {} + +class PassKeySessionExpiredError extends Error {} + +class SrpSetupNotCompleteError extends Error {} diff --git a/mobile/packages/accounts/lib/models/sessions.dart b/mobile/packages/accounts/lib/models/sessions.dart new file mode 100644 index 0000000000..56ba8a31a1 --- /dev/null +++ b/mobile/packages/accounts/lib/models/sessions.dart @@ -0,0 +1,45 @@ +class Sessions { + final List sessions; + + Sessions( + this.sessions, + ); + + factory Sessions.fromMap(Map map) { + if (map["sessions"] == null) { + throw Exception('\'map["sessions"]\' must not be null'); + } + return Sessions( + List.from(map['sessions']?.map((x) => Session.fromMap(x))), + ); + } +} + +class Session { + final String token; + final int creationTime; + final String ip; + final String ua; + final String prettyUA; + final int lastUsedTime; + + Session( + this.token, + this.creationTime, + this.ip, + this.ua, + this.prettyUA, + this.lastUsedTime, + ); + + factory Session.fromMap(Map map) { + return Session( + map['token'], + map['creationTime'], + map['ip'], + map['ua'], + map['prettyUA'], + map['lastUsedTime'], + ); + } +} diff --git a/mobile/packages/accounts/lib/models/set_keys_request.dart b/mobile/packages/accounts/lib/models/set_keys_request.dart new file mode 100644 index 0000000000..e782319f50 --- /dev/null +++ b/mobile/packages/accounts/lib/models/set_keys_request.dart @@ -0,0 +1,25 @@ +class SetKeysRequest { + final String kekSalt; + final String encryptedKey; + final String keyDecryptionNonce; + final int memLimit; + final int opsLimit; + + SetKeysRequest({ + required this.kekSalt, + required this.encryptedKey, + required this.keyDecryptionNonce, + required this.memLimit, + required this.opsLimit, + }); + + Map toMap() { + return { + 'kekSalt': kekSalt, + 'encryptedKey': encryptedKey, + 'keyDecryptionNonce': keyDecryptionNonce, + 'memLimit': memLimit, + 'opsLimit': opsLimit, + }; + } +} diff --git a/mobile/packages/accounts/lib/models/set_recovery_key_request.dart b/mobile/packages/accounts/lib/models/set_recovery_key_request.dart new file mode 100644 index 0000000000..1a03e4e36c --- /dev/null +++ b/mobile/packages/accounts/lib/models/set_recovery_key_request.dart @@ -0,0 +1,22 @@ +class SetRecoveryKeyRequest { + final String masterKeyEncryptedWithRecoveryKey; + final String masterKeyDecryptionNonce; + final String recoveryKeyEncryptedWithMasterKey; + final String recoveryKeyDecryptionNonce; + + SetRecoveryKeyRequest( + this.masterKeyEncryptedWithRecoveryKey, + this.masterKeyDecryptionNonce, + this.recoveryKeyEncryptedWithMasterKey, + this.recoveryKeyDecryptionNonce, + ); + + Map toMap() { + return { + 'masterKeyEncryptedWithRecoveryKey': masterKeyEncryptedWithRecoveryKey, + 'masterKeyDecryptionNonce': masterKeyDecryptionNonce, + 'recoveryKeyEncryptedWithMasterKey': recoveryKeyEncryptedWithMasterKey, + 'recoveryKeyDecryptionNonce': recoveryKeyDecryptionNonce, + }; + } +} diff --git a/mobile/packages/accounts/lib/models/srp.dart b/mobile/packages/accounts/lib/models/srp.dart new file mode 100644 index 0000000000..56b19c1317 --- /dev/null +++ b/mobile/packages/accounts/lib/models/srp.dart @@ -0,0 +1,136 @@ +class SetupSRPRequest { + final String srpUserID; + final String srpSalt; + final String srpVerifier; + final String srpA; + final bool isUpdate; + + SetupSRPRequest({ + required this.srpUserID, + required this.srpSalt, + required this.srpVerifier, + required this.srpA, + required this.isUpdate, + }); + + Map toMap() { + return { + 'srpUserID': srpUserID.toString(), + 'srpSalt': srpSalt, + 'srpVerifier': srpVerifier, + 'srpA': srpA, + 'isUpdate': isUpdate, + }; + } + + factory SetupSRPRequest.fromJson(Map json) { + return SetupSRPRequest( + srpUserID: json['srpUserID'], + srpSalt: json['srpSalt'], + srpVerifier: json['srpVerifier'], + srpA: json['srpA'], + isUpdate: json['isUpdate'], + ); + } +} + +class SetupSRPResponse { + final String setupID; + final String srpB; + + SetupSRPResponse({ + required this.setupID, + required this.srpB, + }); + + Map toMap() { + return { + 'setupID': setupID.toString(), + 'srpB': srpB, + }; + } + + factory SetupSRPResponse.fromJson(Map json) { + return SetupSRPResponse( + setupID: json['setupID'], + srpB: json['srpB'], + ); + } +} + +class CompleteSRPSetupRequest { + final String setupID; + final String srpM1; + + CompleteSRPSetupRequest({ + required this.setupID, + required this.srpM1, + }); + + Map toMap() { + return { + 'setupID': setupID.toString(), + 'srpM1': srpM1, + }; + } + + factory CompleteSRPSetupRequest.fromJson(Map json) { + return CompleteSRPSetupRequest( + setupID: json['setupID'], + srpM1: json['srpM1'], + ); + } +} + +class SrpAttributes { + final String srpUserID; + final String srpSalt; + final int memLimit; + final int opsLimit; + final String kekSalt; + final bool isEmailMFAEnabled; + + SrpAttributes({ + required this.srpUserID, + required this.srpSalt, + required this.memLimit, + required this.opsLimit, + required this.kekSalt, + required this.isEmailMFAEnabled, + }); + + factory SrpAttributes.fromMap(Map map) { + return SrpAttributes( + srpUserID: map['attributes']['srpUserID'], + srpSalt: map['attributes']['srpSalt'], + memLimit: map['attributes']['memLimit'], + opsLimit: map['attributes']['opsLimit'], + kekSalt: map['attributes']['kekSalt'], + isEmailMFAEnabled: map['attributes']['isEmailMFAEnabled'], + ); + } +} + +class CompleteSRPSetupResponse { + final String setupID; + final String srpM2; + + CompleteSRPSetupResponse({ + required this.setupID, + required this.srpM2, + }); + + Map toMap() { + return { + 'setupID': setupID, + 'srpM2': srpM2, + }; + } + + factory CompleteSRPSetupResponse.fromJson(Map json) { + return CompleteSRPSetupResponse( + setupID: json['setupID'], + srpM2: json['srpM2'], + ); + } +} diff --git a/mobile/packages/accounts/lib/models/subscription.dart b/mobile/packages/accounts/lib/models/subscription.dart new file mode 100644 index 0000000000..50735a7c48 --- /dev/null +++ b/mobile/packages/accounts/lib/models/subscription.dart @@ -0,0 +1,117 @@ +import 'dart:convert'; + +const freeProductID = "free"; +const popularProductIDs = ["200gb_yearly", "200gb_monthly"]; +const stripe = "stripe"; +const appStore = "appstore"; +const playStore = "playstore"; + +class Subscription { + final String productID; + final int storage; + final String originalTransactionID; + final String paymentProvider; + final int expiryTime; + final String price; + final String period; + final Attributes? attributes; + + Subscription({ + required this.productID, + required this.storage, + required this.originalTransactionID, + required this.paymentProvider, + required this.expiryTime, + required this.price, + required this.period, + this.attributes, + }); + + bool isValid() { + return expiryTime > DateTime.now().microsecondsSinceEpoch; + } + + bool isCancelled() { + return attributes?.isCancelled ?? false; + } + + bool isPastDue() { + return !isCancelled() && + expiryTime < DateTime.now().microsecondsSinceEpoch && + expiryTime >= + DateTime.now() + .subtract(const Duration(days: 30)) + .microsecondsSinceEpoch; + } + + bool isYearlyPlan() { + return 'year' == period; + } + + bool isFreePlan() { + return productID == freeProductID; + } + + static fromMap(Map? map) { + if (map == null) return null; + return Subscription( + productID: map['productID'], + storage: map['storage'], + originalTransactionID: map['originalTransactionID'], + paymentProvider: map['paymentProvider'], + expiryTime: map['expiryTime'], + price: map['price'], + period: map['period'], + attributes: map["attributes"] != null + ? Attributes.fromMap(map["attributes"]) + : null, + ); + } + + Map toMap() { + return { + 'productID': productID, + 'storage': storage, + 'originalTransactionID': originalTransactionID, + 'paymentProvider': paymentProvider, + 'expiryTime': expiryTime, + 'price': price, + 'period': period, + 'attributes': attributes?.toMap(), + }; + } + + String toJson() => json.encode(toMap()); + + factory Subscription.fromJson(String source) => + Subscription.fromMap(json.decode(source)); +} + +class Attributes { + bool? isCancelled; + String? customerID; + + Attributes({ + this.isCancelled, + this.customerID, + }); + + Map toMap() { + return { + 'isCancelled': isCancelled, + 'customerID': customerID, + }; + } + + factory Attributes.fromMap(Map map) { + return Attributes( + isCancelled: map['isCancelled'], + customerID: map['customerID'], + ); + } + + String toJson() => json.encode(toMap()); + + factory Attributes.fromJson(String source) => + Attributes.fromMap(json.decode(source)); +} diff --git a/mobile/packages/accounts/lib/models/two_factor.dart b/mobile/packages/accounts/lib/models/two_factor.dart new file mode 100644 index 0000000000..45f7c8fde8 --- /dev/null +++ b/mobile/packages/accounts/lib/models/two_factor.dart @@ -0,0 +1,11 @@ +enum TwoFactorType { totp, passkey } + +// ToString for TwoFactorType +String twoFactorTypeToString(TwoFactorType type) { + switch (type) { + case TwoFactorType.totp: + return "totp"; + case TwoFactorType.passkey: + return "passkey"; + } +} diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart new file mode 100644 index 0000000000..bae0311a7f --- /dev/null +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -0,0 +1,249 @@ +import 'dart:convert'; +import 'dart:math'; + +import 'bonus.dart'; +import 'subscription.dart'; + +class UserDetails { + final String email; + final int usage; + final int fileCount; + final int storageBonus; + final int sharedCollectionsCount; + final Subscription subscription; + final FamilyData? familyData; + final ProfileData? profileData; + final BonusData? bonusData; + + const UserDetails( + this.email, + this.usage, + this.fileCount, + this.storageBonus, + this.sharedCollectionsCount, + this.subscription, + this.familyData, + this.profileData, + this.bonusData, + ); + + bool isPartOfFamily() { + return familyData?.members?.isNotEmpty ?? false; + } + + bool hasPaidAddon() { + return bonusData?.getAddOnBonuses().isNotEmpty ?? false; + } + + bool isFamilyAdmin() { + assert(isPartOfFamily(), "verify user is part of family before calling"); + final FamilyMember currentUserMember = familyData!.members! + .firstWhere((element) => element.email.trim() == email.trim()); + return currentUserMember.isAdmin; + } + + // getFamilyOrPersonalUsage will return total usage for family if user + // belong to family group. Otherwise, it will return storage consumed by + // current user + int getFamilyOrPersonalUsage() { + return isPartOfFamily() ? familyData!.getTotalUsage() : usage; + } + + int getFreeStorage() { + final int? memberLimit = familyMemberStorageLimit(); + if (memberLimit != null) { + return max(memberLimit - usage, 0); + } + return max(getTotalStorage() - getFamilyOrPersonalUsage(), 0); + } + + // getTotalStorage will return total storage available including the + // storage bonus + int getTotalStorage() { + return (isPartOfFamily() ? familyData!.storage : subscription.storage) + + storageBonus; + } + + // return the member storage limit if user is part of family and the admin + // has set the storage limit for the user. + int? familyMemberStorageLimit() { + if (isPartOfFamily()) { + try { + final FamilyMember currentUserMember = familyData!.members! + .firstWhere((element) => element.email.trim() == email.trim()); + return currentUserMember.storageLimit; + } catch (e) { + return null; + } + } + return null; + } + + // This is the total storage for which user has paid for. + int getPlanPlusAddonStorage() { + return (isPartOfFamily() ? familyData!.storage : subscription.storage) + + bonusData!.totalAddOnBonus(); + } + + factory UserDetails.fromMap(Map map) { + return UserDetails( + map['email'] as String, + map['usage'] as int, + (map['fileCount'] ?? 0) as int, + (map['storageBonus'] ?? 0) as int, + (map['sharedCollectionsCount'] ?? 0) as int, + Subscription.fromMap(map['subscription']), + FamilyData.fromMap(map['familyData']), + ProfileData.fromJson(map['profileData']), + BonusData.fromJson(map['bonusData']), + ); + } + + Map toMap() { + return { + 'email': email, + 'usage': usage, + 'fileCount': fileCount, + 'storageBonus': storageBonus, + 'sharedCollectionsCount': sharedCollectionsCount, + 'subscription': subscription.toMap(), + 'familyData': familyData?.toMap(), + 'profileData': profileData?.toJson(), + 'bonusData': bonusData?.toJson(), + }; + } + + String toJson() => json.encode(toMap()); + + factory UserDetails.fromJson(String source) => + UserDetails.fromMap(json.decode(source)); +} + +class FamilyMember { + final String email; + final int usage; + final String id; + final bool isAdmin; + final int? storageLimit; + + FamilyMember( + this.email, + this.usage, + this.id, + this.isAdmin, + this.storageLimit, + ); + + factory FamilyMember.fromMap(Map map) { + return FamilyMember( + (map['email'] ?? '') as String, + map['usage'] as int, + map['id'] as String, + map['isAdmin'] as bool, + map['storageLimit'] as int?, + ); + } + + Map toMap() { + return { + 'email': email, + 'usage': usage, + 'id': id, + 'isAdmin': isAdmin, + 'storageLimit': storageLimit, + }; + } + + String toJson() => json.encode(toMap()); + + factory FamilyMember.fromJson(String source) => + FamilyMember.fromMap(json.decode(source)); +} + +class ProfileData { + bool canDisableEmailMFA; + bool isEmailMFAEnabled; + bool isTwoFactorEnabled; + + // Constructor with default values + ProfileData({ + this.canDisableEmailMFA = false, + this.isEmailMFAEnabled = false, + this.isTwoFactorEnabled = false, + }); + + // Factory method to create ProfileData instance from JSON + factory ProfileData.fromJson(Map? json) { + return ProfileData( + canDisableEmailMFA: json?['canDisableEmailMFA'] ?? false, + isEmailMFAEnabled: json?['isEmailMFAEnabled'] ?? false, + isTwoFactorEnabled: json?['isTwoFactorEnabled'] ?? false, + ); + } + + // Method to convert ProfileData instance to JSON + Map toJson() { + return { + 'canDisableEmailMFA': canDisableEmailMFA, + 'isEmailMFAEnabled': isEmailMFAEnabled, + 'isTwoFactorEnabled': isTwoFactorEnabled, + }; + } + + String toJsonString() => json.encode(toJson()); +} + +class FamilyData { + final List? members; + + // Storage available based on the family plan + final int storage; + final int expiryTime; + + FamilyData( + this.members, + this.storage, + this.expiryTime, + ); + + int getTotalUsage() { + return members! + .map((e) => e.usage) + .toList() + .fold(0, (sum, usage) => sum + usage); + } + + FamilyMember? getMemberByID(String id) { + try { + return members!.firstWhere((element) => element.id == id); + } catch (e) { + return null; + } + } + + static fromMap(Map? map) { + if (map == null) return null; + assert(map['members'] != null && map['members'].length >= 0); + final members = List.from( + map['members'].map((x) => FamilyMember.fromMap(x)), + ); + return FamilyData( + members, + map['storage'] as int, + map['expiryTime'] as int, + ); + } + + Map toMap() { + return { + 'members': members?.map((x) => x.toMap()).toList(), + 'storage': storage, + 'expiryTime': expiryTime, + }; + } + + String toJson() => json.encode(toMap()); + + factory FamilyData.fromJson(String source) => + FamilyData.fromMap(json.decode(source)); +} diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart new file mode 100644 index 0000000000..0cc49f25bc --- /dev/null +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -0,0 +1,81 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; + +class ChangeEmailDialog extends StatefulWidget { + const ChangeEmailDialog({super.key}); + + @override + State createState() => _ChangeEmailDialogState(); +} + +class _ChangeEmailDialogState extends State { + String _email = ""; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(context.strings.enterNewEmailHint), + content: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: InputDecoration( + hintText: context.strings.email, + hintStyle: const TextStyle( + color: Colors.white30, + ), + contentPadding: const EdgeInsets.all(12), + ), + onChanged: (value) { + setState(() { + _email = value; + }); + }, + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: _email, + autofocus: true, + ), + ], + ), + ), + actions: [ + TextButton( + child: Text( + context.strings.cancel, + style: const TextStyle( + color: Colors.redAccent, + ), + ), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: Text( + context.strings.verify, + style: const TextStyle( + color: Colors.purple, + ), + ), + onPressed: () { + if (!isValidEmail(_email)) { + showErrorDialog( + context, + context.strings.invalidEmailTitle, + context.strings.invalidEmailMessage, + ); + return; + } + UserService.instance.sendOtt(context, _email, isChangeEmail: true); + }, + ), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart new file mode 100644 index 0000000000..b06ac5e4d5 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -0,0 +1,251 @@ +import 'dart:convert'; + +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; + +class DeleteAccountPage extends StatelessWidget { + final BaseConfiguration configuration; + + const DeleteAccountPage( + this.configuration, { + super.key, + }); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: Text(context.strings.deleteAccount), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: Padding( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), + child: Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Image.asset( + 'assets/broken_heart.png', + width: 200, + ), + const SizedBox( + height: 24, + ), + Center( + child: Text( + context.strings.deleteAccountQuery, + style: Theme.of(context).textTheme.titleMedium, + ), + ), + const SizedBox( + height: 12, + ), + RichText( + // textAlign: TextAlign.center, + text: TextSpan( + children: const [ + TextSpan(text: "Please write to us at "), + TextSpan( + text: "feedback@ente.io", + style: TextStyle(color: Color.fromRGBO(29, 185, 84, 1)), + ), + TextSpan( + text: ", maybe there is a way we can help.", + ), + ], + style: Theme.of(context).textTheme.titleMedium, + ), + ), + const SizedBox( + height: 24, + ), + GradientButton( + text: context.strings.yesSendFeedbackAction, + iconData: Icons.check, + onTap: () async { + await sendEmail( + context, + to: 'feedback@ente.io', + subject: '[Feedback]', + ); + }, + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8), + ), + InkWell( + child: SizedBox( + width: double.infinity, + child: OutlinedButton.icon( + style: OutlinedButton.styleFrom( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + side: const BorderSide( + color: Colors.redAccent, + ), + padding: const EdgeInsets.symmetric( + vertical: 18, + horizontal: 10, + ), + backgroundColor: Colors.white, + ), + label: Text( + context.strings.noDeleteAccountAction, + style: const TextStyle( + color: Colors.redAccent, // same for both themes + ), + textAlign: TextAlign.center, + ), + onPressed: () async => {await _initiateDelete(context)}, + icon: const Icon( + Icons.no_accounts, + color: Colors.redAccent, + ), + ), + ), + ), + ], + ), + ), + ), + ); + } + + Future _initiateDelete(BuildContext context) async { + final deleteChallengeResponse = + await UserService.instance.getDeleteChallenge(context); + if (deleteChallengeResponse == null) { + return; + } + if (deleteChallengeResponse.allowDelete) { + await _confirmAndDelete(context, deleteChallengeResponse); + } else { + await _requestEmailForDeletion(context); + } + } + + Future _confirmAndDelete( + BuildContext context, + DeleteChallengeResponse response, + ) async { + final hasAuthenticated = + await LocalAuthenticationService.instance.requestLocalAuthentication( + context, + context.strings.initiateAccountDeleteTitle, + ); + + await PlatformUtil.refocusWindows(); + + if (hasAuthenticated) { + final choice = await showChoiceDialogOld( + context, + context.strings.confirmAccountDeleteTitle, + context.strings.confirmAccountDeleteMessage, + firstAction: context.strings.cancel, + secondAction: context.strings.delete, + firstActionColor: getEnteColorScheme(context).surface, + secondActionColor: Colors.red, + ); + if (choice != DialogUserChoice.secondChoice) { + return; + } + final decryptChallenge = CryptoUtil.openSealSync( + CryptoUtil.base642bin(response.encryptedChallenge), + CryptoUtil.base642bin( + configuration.getKeyAttributes()!.publicKey, + ), + configuration.getSecretKey()!, + ); + final challengeResponseStr = utf8.decode(decryptChallenge); + await UserService.instance.deleteAccount(context, challengeResponseStr); + } + } + + Future _requestEmailForDeletion(BuildContext context) async { + final AlertDialog alert = AlertDialog( + title: Text( + context.strings.deleteAccount, + style: const TextStyle( + color: Colors.red, + ), + ), + content: RichText( + text: TextSpan( + children: [ + const TextSpan( + text: "Please send an email to ", + ), + TextSpan( + text: "account-deletion@ente.io", + style: TextStyle( + color: Colors.orange[300], + ), + ), + const TextSpan( + text: + " from your registered email address.\n\nYour request will be processed within 72 hours.", + ), + ], + style: TextStyle( + color: getEnteColorScheme(context).surface, + height: 1.5, + fontSize: 16, + ), + ), + ), + actions: [ + TextButton( + child: Text( + context.strings.sendEmail, + style: const TextStyle( + color: Colors.red, + ), + ), + onPressed: () async { + Navigator.of(context, rootNavigator: true).pop('dialog'); + await sendEmail( + context, + to: 'account-deletion@ente.io', + subject: '[Delete account]', + ); + }, + ), + TextButton( + child: Text( + context.strings.ok, + style: TextStyle( + color: getEnteColorScheme(context).surface, + ), + ), + onPressed: () { + Navigator.of(context, rootNavigator: true).pop('dialog'); + }, + ), + ], + ); + // ignore: unawaited_futures + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/email_entry_page.dart b/mobile/packages/accounts/lib/pages/email_entry_page.dart new file mode 100644 index 0000000000..7c922d793d --- /dev/null +++ b/mobile/packages/accounts/lib/pages/email_entry_page.dart @@ -0,0 +1,509 @@ +import 'package:email_validator/email_validator.dart'; +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:password_strength/password_strength.dart'; +import 'package:step_progress_indicator/step_progress_indicator.dart'; +import "package:styled_text/styled_text.dart"; + +class EmailEntryPage extends StatefulWidget { + final BaseConfiguration config; + + const EmailEntryPage(this.config, {super.key}); + + @override + State createState() => _EmailEntryPageState(); +} + +class _EmailEntryPageState extends State { + static const kMildPasswordStrengthThreshold = 0.4; + static const kStrongPasswordStrengthThreshold = 0.7; + + final _passwordController1 = TextEditingController(); + final _passwordController2 = TextEditingController(); + Color? _validFieldValueColor; + + String? _email; + String? _password; + String _cnfPassword = ''; + String _referralSource = ''; + double _passwordStrength = 0.0; + bool _emailIsValid = false; + bool _hasAgreedToTOS = true; + bool _hasAgreedToE2E = false; + bool _password1Visible = false; + bool _password2Visible = false; + bool _passwordsMatch = false; + + final _password1FocusNode = FocusNode(); + final _password2FocusNode = FocusNode(); + bool _password1InFocus = false; + bool _password2InFocus = false; + bool _passwordIsValid = false; + + @override + void initState() { + _email = widget.config.getEmail(); + _password1FocusNode.addListener(() { + setState(() { + _password1InFocus = _password1FocusNode.hasFocus; + }); + }); + _password2FocusNode.addListener(() { + setState(() { + _password2InFocus = _password2FocusNode.hasFocus; + }); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + // Initialize theme-aware color + final colorScheme = getEnteColorScheme(context); + _validFieldValueColor = colorScheme.primary300.withOpacity(0.2); + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + final appBar = AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: Material( + type: MaterialType.transparency, + child: StepProgressIndicator( + totalSteps: 4, + currentStep: 1, + selectedColor: getEnteColorScheme(context).alternativeColor, + roundedEdges: const Radius.circular(10), + unselectedColor: + getEnteColorScheme(context).stepProgressUnselectedColor, + ), + ), + ); + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: appBar, + body: _getBody(), + floatingActionButton: DynamicFAB( + isKeypadOpen: isKeypadOpen, + isFormValid: _isFormValid(), + buttonText: context.strings.createAccount, + onPressedFunction: () { + UserService.instance.setEmail(_email!); + widget.config.setVolatilePassword(_passwordController1.text); + UserService.instance.setRefSource(_referralSource); + UserService.instance.sendOtt( + context, + _email!, + isCreateAccountScreen: true, + purpose: "signup", + ); + FocusScope.of(context).unfocus(); + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Widget _getBody() { + var passwordStrengthText = context.strings.weakStrength; + var passwordStrengthColor = Colors.redAccent; + if (_passwordStrength > kStrongPasswordStrengthThreshold) { + passwordStrengthText = context.strings.strongStrength; + passwordStrengthColor = Colors.greenAccent; + } else if (_passwordStrength > kMildPasswordStrengthThreshold) { + passwordStrengthText = context.strings.moderateStrength; + passwordStrengthColor = Colors.orangeAccent; + } + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: ListView( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Text( + context.strings.createNewAccount, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + style: Theme.of(context).textTheme.titleMedium, + autofillHints: const [AutofillHints.email], + decoration: InputDecoration( + fillColor: _emailIsValid ? _validFieldValueColor : null, + filled: true, + hintText: context.strings.email, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _emailIsValid + ? Icon( + Icons.check, + size: 20, + color: getEnteColorScheme(context).primary300, + ) + : null, + ), + onChanged: (value) { + _email = value.trim(); + if (_emailIsValid != EmailValidator.validate(_email!)) { + setState(() { + _emailIsValid = EmailValidator.validate(_email!); + }); + } + }, + autocorrect: false, + keyboardType: TextInputType.emailAddress, + //initialValue: _email, + textInputAction: TextInputAction.next, + ), + ), + const Padding(padding: EdgeInsets.all(4)), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + controller: _passwordController1, + obscureText: !_password1Visible, + enableSuggestions: true, + autofillHints: const [AutofillHints.newPassword], + decoration: InputDecoration( + fillColor: + _passwordIsValid ? _validFieldValueColor : null, + filled: true, + hintText: context.strings.password, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + suffixIcon: _password1InFocus + ? IconButton( + icon: Icon( + _password1Visible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _password1Visible = !_password1Visible; + }); + }, + ) + : _passwordIsValid + ? Icon( + Icons.check, + color: getEnteColorScheme(context).primary300, + ) + : null, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + ), + focusNode: _password1FocusNode, + onChanged: (password) { + if (password != _password) { + setState(() { + _password = password; + _passwordStrength = + estimatePasswordStrength(password); + _passwordIsValid = _passwordStrength >= + kMildPasswordStrengthThreshold; + _passwordsMatch = _password == _cnfPassword; + }); + } + }, + onEditingComplete: () { + _password1FocusNode.unfocus(); + _password2FocusNode.requestFocus(); + TextInput.finishAutofillContext(); + }, + ), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + keyboardType: TextInputType.visiblePassword, + controller: _passwordController2, + obscureText: !_password2Visible, + autofillHints: const [AutofillHints.newPassword], + onEditingComplete: () => TextInput.finishAutofillContext(), + decoration: InputDecoration( + fillColor: _passwordsMatch && _passwordIsValid + ? _validFieldValueColor + : null, + filled: true, + hintText: context.strings.confirmPassword, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + suffixIcon: _password2InFocus + ? IconButton( + icon: Icon( + _password2Visible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _password2Visible = !_password2Visible; + }); + }, + ) + : _passwordsMatch + ? Icon( + Icons.check, + color: getEnteColorScheme(context).primary300, + ) + : null, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + ), + focusNode: _password2FocusNode, + onChanged: (cnfPassword) { + setState(() { + _cnfPassword = cnfPassword; + if (_password != null || _password != '') { + _passwordsMatch = _password == _cnfPassword; + } + }); + }, + ), + ), + Opacity( + opacity: (_password != '') && _password1InFocus ? 1 : 0, + child: Padding( + padding: + const EdgeInsets.symmetric(horizontal: 24, vertical: 8), + child: Text( + context.strings.passwordStrength(passwordStrengthText), + style: TextStyle( + color: passwordStrengthColor, + fontWeight: FontWeight.w500, + fontSize: 12, + ), + ), + ), + ), + const SizedBox(height: 4), + Padding( + padding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 20), + child: Text( + context.strings.hearUsWhereTitle, + style: getEnteTextTheme(context).smallFaint, + ), + ), + const SizedBox(height: 4), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + style: Theme.of(context).textTheme.titleMedium, + decoration: InputDecoration( + fillColor: null, + filled: true, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: InkWell( + onTap: () { + showToast( + context, + context.strings.hearUsExplanation, + ); + }, + child: Icon( + Icons.info_outline_rounded, + color: getEnteColorScheme(context).strokeMuted, + ), + ), + ), + onChanged: (value) { + _referralSource = value.trim(); + }, + autocorrect: false, + keyboardType: TextInputType.text, + textInputAction: TextInputAction.next, + ), + ), + const Divider(thickness: 1), + const SizedBox(height: 12), + _getAgreement(), + const SizedBox(height: 40), + ], + ), + ), + ), + ], + ); + } + + Container _getAgreement() { + return Container( + padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), + child: Column( + children: [ + _getTOSAgreement(), + _getPasswordAgreement(), + ], + ), + ); + } + + Widget _getTOSAgreement() { + return GestureDetector( + onTap: () { + setState(() { + _hasAgreedToTOS = !_hasAgreedToTOS; + }); + }, + behavior: HitTestBehavior.translucent, + child: Row( + children: [ + Checkbox( + value: _hasAgreedToTOS, + side: CheckboxTheme.of(context).side, + onChanged: (value) { + setState(() { + _hasAgreedToTOS = value!; + }); + }, + ), + Expanded( + child: StyledText( + text: context.strings.signUpTerms, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 12), + tags: { + 'u-terms': StyledTextActionTag( + (String? text, Map attrs) => + PlatformUtil.openWebView( + context, + context.strings.termsOfServicesTitle, + "https://ente.io/terms", + ), + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + 'u-policy': StyledTextActionTag( + (String? text, Map attrs) => + PlatformUtil.openWebView( + context, + context.strings.privacyPolicyTitle, + "https://ente.io/privacy", + ), + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + }, + ), + ), + ], + ), + ); + } + + Widget _getPasswordAgreement() { + return GestureDetector( + onTap: () { + setState(() { + _hasAgreedToE2E = !_hasAgreedToE2E; + }); + }, + behavior: HitTestBehavior.translucent, + child: Row( + children: [ + Checkbox( + value: _hasAgreedToE2E, + side: CheckboxTheme.of(context).side, + onChanged: (value) { + setState(() { + _hasAgreedToE2E = value!; + }); + }, + ), + Expanded( + child: StyledText( + text: context.strings.ackPasswordLostWarning, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 12), + tags: { + 'underline': StyledTextActionTag( + (String? text, Map attrs) => + PlatformUtil.openWebView( + context, + context.strings.encryption, + "https://ente.io/architecture", + ), + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + }, + ), + ), + ], + ), + ); + } + + bool _isFormValid() { + return _emailIsValid && + _passwordsMatch && + _hasAgreedToTOS && + _hasAgreedToE2E && + _passwordIsValid; + } + + void showToast(BuildContext context, String hearUsExplanation) {} +} diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart new file mode 100644 index 0000000000..17737cc0e0 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -0,0 +1,221 @@ +import 'package:email_validator/email_validator.dart'; +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_accounts/models/errors.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/platform_util.dart'; +import "package:ente_strings/ente_strings.dart"; +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import "package:styled_text/styled_text.dart"; + +class LoginPage extends StatefulWidget { + final BaseConfiguration config; + + const LoginPage(this.config, {super.key}); + + @override + State createState() => _LoginPageState(); +} + +class _LoginPageState extends State { + bool _emailIsValid = false; + String? _email; + Color? _emailInputFieldColor; + final Logger _logger = Logger('_LoginPageState'); + + Future onPressed() async { + await UserService.instance.setEmail(_email!); + widget.config.resetVolatilePassword(); + SrpAttributes? attr; + bool isEmailVerificationEnabled = true; + try { + attr = await UserService.instance.getSrpAttributes(_email!); + isEmailVerificationEnabled = attr.isEmailMFAEnabled; + } catch (e) { + if (e is! SrpSetupNotCompleteError) { + _logger.severe('Error getting SRP attributes', e); + } + } + if (attr != null && !isEmailVerificationEnabled) { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginPasswordVerificationPage( + widget.config, + attr!, + ); + }, + ), + ); + } else { + await UserService.instance.sendOtt( + context, + _email!, + isCreateAccountScreen: false, + purpose: 'login', + ); + } + FocusScope.of(context).unfocus(); + } + + @override + void initState() { + _email = widget.config.getEmail(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: _getBody(), + floatingActionButton: DynamicFAB( + isKeypadOpen: isKeypadOpen, + isFormValid: _emailIsValid, + buttonText: context.strings.logInLabel, + onPressedFunction: onPressed, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Widget _getBody() { + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: ListView( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Text( + context.strings.welcomeBack, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), + child: TextFormField( + autofillHints: const [AutofillHints.email], + onFieldSubmitted: + _emailIsValid ? (value) => onPressed() : null, + decoration: InputDecoration( + fillColor: _emailInputFieldColor, + filled: true, + hintText: context.strings.email, + contentPadding: const EdgeInsets.symmetric( + horizontal: 15, + vertical: 15, + ), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _emailIsValid + ? Icon( + Icons.check, + size: 20, + color: getEnteColorScheme(context).primary300, + ) + : null, + ), + onChanged: (value) { + setState(() { + _email = value.trim(); + _emailIsValid = EmailValidator.validate(_email!); + if (_emailIsValid) { + _emailInputFieldColor = getEnteColorScheme(context) + .primary300 + .withOpacity(0.2); + } else { + _emailInputFieldColor = null; + } + }); + }, + autocorrect: false, + keyboardType: TextInputType.emailAddress, + //initialValue: _email, + autofocus: true, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 18), + child: Divider( + thickness: 1, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + children: [ + Expanded( + flex: 5, + child: StyledText( + text: context.strings.loginTerms, + style: getEnteTextTheme(context).small, + tags: { + 'u-terms': StyledTextActionTag( + (String? text, Map attrs) => + PlatformUtil.openWebView( + context, + context.strings.termsOfServicesTitle, + "https://ente.io/terms", + ), + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + 'u-policy': StyledTextActionTag( + (String? text, Map attrs) => + PlatformUtil.openWebView( + context, + context.strings.privacyPolicyTitle, + "https://ente.io/privacy", + ), + style: const TextStyle( + decoration: TextDecoration.underline, + ), + ), + }, + ), + ), + Expanded( + flex: 2, + child: Container(), + ), + ], + ), + ), + ], + ), + ), + ), + const Padding(padding: EdgeInsets.all(8)), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart new file mode 100644 index 0000000000..6c0144f3b0 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -0,0 +1,349 @@ +import "package:dio/dio.dart"; +import "package:ente_accounts/ente_accounts.dart"; +import "package:ente_configuration/base_configuration.dart"; +import "package:ente_ui/components/buttons/button_widget.dart"; +import "package:ente_ui/components/buttons/dynamic_fab.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_ui/utils/dialog_util.dart"; +import "package:ente_utils/email_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import 'package:flutter/material.dart'; +import "package:logging/logging.dart"; + +// LoginPasswordVerificationPage is a page that allows the user to enter their password to verify their identity. +// If the password is correct, then the user is either directed to +// PasswordReentryPage (if the user has not yet set up 2FA) or TwoFactorAuthenticationPage (if the user has set up 2FA). +// In the PasswordReentryPage, the password is auto-filled based on the +// volatile password. +class LoginPasswordVerificationPage extends StatefulWidget { + final BaseConfiguration config; + final SrpAttributes srpAttributes; + const LoginPasswordVerificationPage( + this.config, + this.srpAttributes, { + super.key, + }); + + @override + State createState() => + _LoginPasswordVerificationPageState(); +} + +class _LoginPasswordVerificationPageState + extends State { + final _logger = Logger((_LoginPasswordVerificationPageState).toString()); + final _passwordController = TextEditingController(); + final FocusNode _passwordFocusNode = FocusNode(); + String? email; + bool _passwordInFocus = false; + bool _passwordVisible = false; + + Future onPressed() async { + FocusScope.of(context).unfocus(); + await verifyPassword(context, _passwordController.text); + } + + @override + void initState() { + super.initState(); + email = widget.config.getEmail(); + _passwordFocusNode.addListener(() { + setState(() { + _passwordInFocus = _passwordFocusNode.hasFocus; + }); + }); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: _getBody(), + floatingActionButton: DynamicFAB( + key: const ValueKey("verifyPasswordButton"), + isKeypadOpen: isKeypadOpen, + isFormValid: _passwordController.text.isNotEmpty, + buttonText: context.strings.logInLabel, + onPressedFunction: onPressed, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Future verifyPassword(BuildContext context, String password) async { + final dialog = createProgressDialog( + context, + context.strings.pleaseWait, + isDismissible: true, + ); + await dialog.show(); + try { + await UserService.instance.verifyEmailViaPassword( + context, + widget.srpAttributes, + password, + dialog, + ); + } on DioException catch (e, s) { + await dialog.hide(); + if (e.response != null && e.response!.statusCode == 401) { + _logger.severe('server reject, failed verify SRP login', e, s); + await _showContactSupportDialog( + context, + context.strings.incorrectPasswordTitle, + context.strings.pleaseTryAgain, + ); + } else { + _logger.severe('API failure during SRP login', e, s); + if (e.type == DioExceptionType.connectionError) { + await _showContactSupportDialog( + context, + context.strings.noInternetConnection, + context.strings.pleaseCheckYourInternetConnectionAndTryAgain, + ); + } else { + await _showContactSupportDialog( + context, + context.strings.oops, + context.strings.verificationFailedPleaseTryAgain, + ); + } + } + } catch (e, s) { + _logger.info('error during loginViaPassword', e); + await dialog.hide(); + if (e is LoginKeyDerivationError) { + _logger.severe('loginKey derivation error', e, s); + // LoginKey err, perform regular login via ott verification + await UserService.instance.sendOtt( + context, + email!, + isCreateAccountScreen: true, + ); + return; + } else if (e is KeyDerivationError) { + // device is not powerful enough to perform derive key + final dialogChoice = await showChoiceDialog( + context, + title: context.strings.recreatePasswordTitle, + body: context.strings.recreatePasswordBody, + firstButtonLabel: context.strings.useRecoveryKey, + ); + if (dialogChoice!.action == ButtonAction.first) { + await UserService.instance.sendOtt( + context, + email!, + isResetPasswordScreen: true, + ); + } + return; + } else { + _logger.severe('unexpected error while verifying password', e, s); + await _showContactSupportDialog( + context, + context.strings.oops, + context.strings.verificationFailedPleaseTryAgain, + ); + } + } + } + + Future _showContactSupportDialog( + BuildContext context, + String title, + String message, + ) async { + final dialogChoice = await showChoiceDialog( + context, + title: title, + body: message, + firstButtonLabel: context.strings.contactSupport, + secondButtonLabel: context.strings.ok, + ); + if (dialogChoice!.action == ButtonAction.first) { + await sendLogs( + context, + context.strings.contactSupport, + postShare: () {}, + ); + } + } + + Widget _getBody() { + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.only(top: 30, left: 20, right: 20), + child: Text( + context.strings.enterPassword, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.only( + bottom: 30, + left: 22, + right: 20, + ), + child: Text( + email ?? '', + style: getEnteTextTheme(context).smallMuted, + ), + ), + Visibility( + // hidden textForm for suggesting auto-fill service for saving + // password + visible: false, + child: TextFormField( + autofillHints: const [ + AutofillHints.email, + ], + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: email, + textInputAction: TextInputAction.next, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), + child: TextFormField( + onFieldSubmitted: _passwordController.text.isNotEmpty + ? (_) => onPressed() + : null, + key: const ValueKey("passwordInputField"), + autofillHints: const [AutofillHints.password], + decoration: InputDecoration( + hintText: context.strings.enterYourPasswordHint, + filled: true, + contentPadding: const EdgeInsets.all(20), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _passwordInFocus + ? IconButton( + icon: Icon( + _passwordVisible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ) + : null, + ), + style: const TextStyle( + fontSize: 14, + ), + controller: _passwordController, + autofocus: true, + autocorrect: false, + obscureText: !_passwordVisible, + keyboardType: TextInputType.visiblePassword, + focusNode: _passwordFocusNode, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 18), + child: Divider( + thickness: 1, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + await UserService.instance.sendOtt( + context, + email!, + isResetPasswordScreen: true, + ); + }, + child: Center( + child: Text( + context.strings.forgotPassword, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + final dialog = createProgressDialog( + context, + context.strings.pleaseWait, + ); + await dialog.show(); + await widget.config.logout(); + await dialog.hide(); + Navigator.of(context) + .popUntil((route) => route.isFirst); + }, + child: Center( + child: Text( + context.strings.changeEmail, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart new file mode 100644 index 0000000000..442401fbd7 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -0,0 +1,221 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:step_progress_indicator/step_progress_indicator.dart'; +import 'package:styled_text/styled_text.dart'; + +class OTTVerificationPage extends StatefulWidget { + final String email; + final bool isChangeEmail; + final bool isCreateAccountScreen; + final bool isResetPasswordScreen; + + const OTTVerificationPage( + this.email, { + this.isChangeEmail = false, + this.isCreateAccountScreen = false, + this.isResetPasswordScreen = false, + super.key, + }); + + @override + State createState() => _OTTVerificationPageState(); +} + +class _OTTVerificationPageState extends State { + final _verificationCodeController = TextEditingController(); + + Future onPressed() async { + if (widget.isChangeEmail) { + await UserService.instance.changeEmail( + context, + widget.email, + _verificationCodeController.text, + ); + } else { + await UserService.instance.verifyEmail( + context, + _verificationCodeController.text, + isResettingPasswordScreen: widget.isResetPasswordScreen, + ); + } + FocusScope.of(context).unfocus(); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + title: widget.isCreateAccountScreen + ? Material( + type: MaterialType.transparency, + child: StepProgressIndicator( + totalSteps: 4, + currentStep: 2, + selectedColor: getEnteColorScheme(context).alternativeColor, + roundedEdges: const Radius.circular(10), + unselectedColor: + getEnteColorScheme(context).stepProgressUnselectedColor, + ), + ) + : null, + ), + body: _getBody(), + floatingActionButton: DynamicFAB( + isKeypadOpen: isKeypadOpen, + isFormValid: _verificationCodeController.text.isNotEmpty, + buttonText: context.strings.verify, + onPressedFunction: onPressed, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Widget _getBody() { + return ListView( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(20, 30, 20, 15), + child: Text( + context.strings.verifyEmail, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), + child: Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 12), + child: StyledText( + text: + context.strings.weHaveSendEmailTo(widget.email), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 14), + tags: { + 'green': StyledTextTag( + style: TextStyle( + color: getEnteColorScheme(context) + .alternativeColor, + ), + ), + }, + ), + ), + widget.isResetPasswordScreen + ? Text( + context.strings.toResetVerifyEmail, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 14), + ) + : Text( + context.strings.checkInboxAndSpamFolder, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 14), + ), + ], + ), + ), + SizedBox( + width: MediaQuery.of(context).size.width * 0.2, + height: 1, + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 16, 20, 16), + child: TextFormField( + style: Theme.of(context).textTheme.titleMedium, + onFieldSubmitted: _verificationCodeController.text.isNotEmpty + ? (_) => onPressed() + : null, + decoration: InputDecoration( + filled: true, + hintText: context.strings.tapToEnterCode, + contentPadding: const EdgeInsets.all(15), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + ), + controller: _verificationCodeController, + autofocus: true, + autocorrect: false, + keyboardType: TextInputType.number, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Divider( + thickness: 1, + ), + Padding( + padding: const EdgeInsets.all(20), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () { + UserService.instance.sendOtt( + context, + widget.email, + isCreateAccountScreen: widget.isCreateAccountScreen, + isChangeEmail: widget.isChangeEmail, + isResetPasswordScreen: widget.isResetPasswordScreen, + ); + }, + child: Text( + context.strings.resendEmail, + style: Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ], + ), + ), + ], + ), + ], + ); + // ); + } +} diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart new file mode 100644 index 0000000000..584be1bceb --- /dev/null +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -0,0 +1,234 @@ +import 'dart:convert'; + +import 'package:app_links/app_links.dart'; +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_accounts/models/errors.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_utils/navigation_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +class PasskeyPage extends StatefulWidget { + final BaseConfiguration config; + final String sessionID; + final String totp2FASessionID; + final String accountsUrl; + + const PasskeyPage( + this.config, + this.sessionID, { + required this.totp2FASessionID, + required this.accountsUrl, + super.key, + }); + + @override + State createState() => _PasskeyPageState(); +} + +class _PasskeyPageState extends State { + final Logger _logger = Logger("PasskeyPage"); + + @override + void initState() { + launchPasskey(); + _initDeepLinks(); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + Future launchPasskey() async { + await launchUrlString( + "${widget.accountsUrl}/passkeys/verify?" + "passkeySessionID=${widget.sessionID}" + "&redirect=enteauth://passkey" + "&clientPackage=io.ente.auth", + mode: LaunchMode.externalApplication, + ); + } + + Future checkStatus() async { + late dynamic response; + try { + response = await UserService.instance + .getTokenForPasskeySession(widget.sessionID); + } on PassKeySessionNotVerifiedError { + showToast(context, context.strings.passKeyPendingVerification); + return; + } on PassKeySessionExpiredError { + await showErrorDialog( + context, + context.strings.loginSessionExpired, + context.strings.loginSessionExpiredDetails, + ); + Navigator.of(context).pop(); + return; + } catch (e, s) { + _logger.severe("failed to check status", e, s); + showGenericErrorDialog(context: context, error: e).ignore(); + return; + } + await UserService.instance.onPassKeyVerified(context, response); + } + + Future _handleDeeplink(String? link) async { + if (!context.mounted || + widget.config.hasConfiguredAccount() || + link == null) { + _logger.warning( + 'ignored deeplink: contextMounted ${context.mounted}', + ); + return; + } + try { + if (mounted && link.toLowerCase().startsWith("enteauth://passkey")) { + if (widget.config.isLoggedIn()) { + _logger.info('ignored deeplink: already configured'); + showToast(context, 'Account is already configured.'); + return; + } + final parsedUri = Uri.parse(link); + final sessionID = parsedUri.queryParameters['passkeySessionID']; + if (sessionID != widget.sessionID) { + showToast(context, "Session ID mismatch"); + _logger.warning('ignored deeplink: sessionID mismatch'); + return; + } + final String? authResponse = parsedUri.queryParameters['response']; + String base64String = authResponse!.toString(); + while (base64String.length % 4 != 0) { + base64String += '='; + } + final res = utf8.decode(base64.decode(base64String)); + final json = jsonDecode(res) as Map; + await UserService.instance.onPassKeyVerified(context, json); + } else { + _logger.info('ignored deeplink: $link mounted $mounted'); + } + } catch (e, s) { + _logger.severe('passKey: failed to handle deeplink', e, s); + showGenericErrorDialog(context: context, error: e).ignore(); + } + } + + Future _initDeepLinks() async { + final appLinks = AppLinks(); + // Attach a listener to the stream + appLinks.stringLinkStream.listen( + _handleDeeplink, + onError: (err) { + _logger.severe(err); + }, + ); + return false; + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + context.strings.passkeyAuthTitle, + ), + ), + body: _getBody(), + ); + } + + Widget _getBody() { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + context.strings.waitingForVerification, + style: const TextStyle( + height: 1.4, + fontSize: 16, + ), + ), + const SizedBox(height: 16), + ButtonWidget( + buttonType: ButtonType.primary, + labelText: context.strings.tryAgain, + onTap: () => launchPasskey(), + ), + const SizedBox(height: 16), + ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.strings.checkStatus, + onTap: () async { + try { + await checkStatus(); + } catch (e) { + debugPrint('failed to check status %e'); + showGenericErrorDialog(context: context, error: e).ignore(); + } + }, + shouldSurfaceExecutionStates: true, + ), + const Padding(padding: EdgeInsets.all(30)), + if (widget.totp2FASessionID.isNotEmpty) + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + routeToPage( + context, + TwoFactorAuthenticationPage( + widget.totp2FASessionID, + ), + ); + }, + child: Container( + padding: const EdgeInsets.all(10), + child: Center( + child: Text( + context.strings.loginWithTOTP, + style: const TextStyle( + decoration: TextDecoration.underline, + fontSize: 12, + ), + ), + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + UserService.instance.recoverTwoFactor( + context, + widget.sessionID, + TwoFactorType.passkey, + ); + }, + child: Container( + padding: const EdgeInsets.all(10), + child: Center( + child: Text( + context.strings.recoverAccount, + style: const TextStyle( + decoration: TextDecoration.underline, + fontSize: 12, + ), + ), + ), + ), + ), + ], + ), + ), + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart new file mode 100644 index 0000000000..bf171e3cc3 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -0,0 +1,520 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/pages/base_home_page.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_utils/navigation_util.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; +import 'package:password_strength/password_strength.dart'; +import 'package:styled_text/styled_text.dart'; + +enum PasswordEntryMode { + set, + update, + reset, +} + +class PasswordEntryPage extends StatefulWidget { + final BaseConfiguration config; + final PasswordEntryMode mode; + final BaseHomePage homePage; + + const PasswordEntryPage( + this.config, + this.mode, + this.homePage, { + super.key, + }); + + @override + State createState() => _PasswordEntryPageState(); +} + +class _PasswordEntryPageState extends State { + static const kMildPasswordStrengthThreshold = 0.4; + static const kStrongPasswordStrengthThreshold = 0.7; + + final _logger = Logger((_PasswordEntryPageState).toString()); + final _passwordController1 = TextEditingController(), + _passwordController2 = TextEditingController(); + Color? _validFieldValueColor; + String? _volatilePassword; + String _passwordInInputBox = ''; + String _passwordInInputConfirmationBox = ''; + double _passwordStrength = 0.0; + bool _password1Visible = false; + bool _password2Visible = false; + final _password1FocusNode = FocusNode(); + final _password2FocusNode = FocusNode(); + bool _password1InFocus = false; + bool _password2InFocus = false; + + bool _passwordsMatch = false; + bool _isPasswordValid = false; + + @override + void initState() { + super.initState(); + _volatilePassword = widget.config.getVolatilePassword(); + if (_volatilePassword != null) { + Future.delayed( + Duration.zero, + () => _showRecoveryCodeDialog( + _volatilePassword!, + usingVolatilePassword: true, + ), + ); + } + _password1FocusNode.addListener(() { + setState(() { + _password1InFocus = _password1FocusNode.hasFocus; + }); + }); + _password2FocusNode.addListener(() { + setState(() { + _password2InFocus = _password2FocusNode.hasFocus; + }); + }); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + // Initialize theme-aware color + final colorScheme = getEnteColorScheme(context); + _validFieldValueColor = colorScheme.fillFaint.withOpacity(0.5); + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + String title = context.strings.setPasswordTitle; + if (widget.mode == PasswordEntryMode.update) { + title = context.strings.changePasswordTitle; + } else if (widget.mode == PasswordEntryMode.reset) { + title = context.strings.resetPasswordTitle; + } else if (_volatilePassword != null) { + title = context.strings.encryptionKeys; + } + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + leading: widget.mode == PasswordEntryMode.reset + ? Container() + : IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + elevation: 0, + ), + body: _getBody(title), + floatingActionButton: DynamicFAB( + isKeypadOpen: isKeypadOpen, + isFormValid: _passwordsMatch && _isPasswordValid, + buttonText: title, + onPressedFunction: () { + if (widget.mode == PasswordEntryMode.set) { + _showRecoveryCodeDialog(_passwordController1.text); + } else { + _updatePassword(); + } + FocusScope.of(context).unfocus(); + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Widget _getBody(String buttonTextAndHeading) { + final email = widget.config.getEmail(); + var passwordStrengthText = context.strings.weakStrength; + var passwordStrengthColor = Colors.redAccent; + if (_passwordStrength > kStrongPasswordStrengthThreshold) { + passwordStrengthText = context.strings.strongStrength; + passwordStrengthColor = Colors.greenAccent; + } else if (_passwordStrength > kMildPasswordStrengthThreshold) { + passwordStrengthText = context.strings.moderateStrength; + passwordStrengthColor = Colors.orangeAccent; + } + if (_volatilePassword != null) { + return Container(); + } + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + vertical: 30, + horizontal: 20, + ), + child: Text( + buttonTextAndHeading, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Text( + widget.mode == PasswordEntryMode.set + ? context.strings.enterPasswordToEncrypt + : context.strings.enterNewPasswordToEncrypt, + textAlign: TextAlign.start, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 14), + ), + ), + const Padding(padding: EdgeInsets.all(8)), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: StyledText( + text: context.strings.passwordWarning, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontSize: 14), + tags: { + 'underline': StyledTextTag( + style: + Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + }, + ), + ), + const Padding(padding: EdgeInsets.all(12)), + Visibility( + // hidden textForm for suggesting auto-fill service for saving + // password + visible: false, + child: TextFormField( + autofillHints: const [ + AutofillHints.email, + ], + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: email, + textInputAction: TextInputAction.next, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + autofillHints: const [AutofillHints.newPassword], + onFieldSubmitted: (_) { + do { + FocusScope.of(context).nextFocus(); + } while (FocusScope.of(context).focusedChild!.context == + null); + }, + decoration: InputDecoration( + fillColor: + _isPasswordValid ? _validFieldValueColor : null, + filled: true, + hintText: context.strings.password, + contentPadding: const EdgeInsets.all(20), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _password1InFocus + ? IconButton( + icon: Icon( + _password1Visible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _password1Visible = !_password1Visible; + }); + }, + ) + : _isPasswordValid + ? Icon( + Icons.check, + color: + getEnteColorScheme(context).primary300, + ) + : null, + ), + obscureText: !_password1Visible, + controller: _passwordController1, + autofocus: false, + autocorrect: false, + keyboardType: TextInputType.visiblePassword, + onChanged: (password) { + setState(() { + _passwordInInputBox = password; + _passwordStrength = + estimatePasswordStrength(password); + _isPasswordValid = _passwordStrength >= + kMildPasswordStrengthThreshold; + _passwordsMatch = _passwordInInputBox == + _passwordInInputConfirmationBox; + }); + }, + textInputAction: TextInputAction.next, + focusNode: _password1FocusNode, + ), + ), + const SizedBox(height: 8), + Padding( + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: TextFormField( + keyboardType: TextInputType.visiblePassword, + controller: _passwordController2, + obscureText: !_password2Visible, + autofillHints: const [AutofillHints.newPassword], + onEditingComplete: () => + TextInput.finishAutofillContext(), + decoration: InputDecoration( + fillColor: + _passwordsMatch ? _validFieldValueColor : null, + filled: true, + hintText: context.strings.confirmPassword, + contentPadding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 20, + ), + suffixIcon: _password2InFocus + ? IconButton( + icon: Icon( + _password2Visible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _password2Visible = !_password2Visible; + }); + }, + ) + : _passwordsMatch + ? Icon( + Icons.check, + color: + getEnteColorScheme(context).primary300, + ) + : null, + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + ), + focusNode: _password2FocusNode, + onChanged: (cnfPassword) { + setState(() { + _passwordInInputConfirmationBox = cnfPassword; + if (_passwordInInputBox != '') { + _passwordsMatch = _passwordInInputBox == + _passwordInInputConfirmationBox; + } + }); + }, + ), + ), + Opacity( + opacity: (_passwordInInputBox != '') && _password1InFocus + ? 1 + : 0, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 20, + vertical: 8, + ), + child: Text( + context.strings.passwordStrength(passwordStrengthText), + style: TextStyle( + color: passwordStrengthColor, + ), + ), + ), + ), + const SizedBox(height: 8), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + PlatformUtil.openWebView( + context, + context.strings.howItWorks, + "https://ente.io/architecture", + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: RichText( + text: TextSpan( + text: context.strings.howItWorks, + style: + Theme.of(context).textTheme.titleMedium!.copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ), + const Padding(padding: EdgeInsets.all(20)), + ], + ), + ), + ), + ), + ], + ); + } + + void _updatePassword() async { + final logOutFromOthers = await logOutFromOtherDevices(context); + final dialog = + createProgressDialog(context, context.strings.generatingEncryptionKeys); + await dialog.show(); + try { + final result = await widget.config + .getAttributesForNewPassword(_passwordController1.text); + await UserService.instance.updateKeyAttributes( + result.item1, + result.item2, + logoutOtherDevices: logOutFromOthers, + ); + await dialog.hide(); + showShortToast(context, context.strings.passwordChangedSuccessfully); + Navigator.of(context).pop(); + if (widget.mode == PasswordEntryMode.reset) { + Navigator.of(context).popUntil((route) => route.isFirst); + } + } catch (e, s) { + _logger.severe(e, s); + await dialog.hide(); + // ignore: unawaited_futures + showGenericErrorDialog( + context: context, + error: e, + ); + } + } + + Future logOutFromOtherDevices(BuildContext context) async { + bool logOutFromOther = true; + await showChoiceDialog( + context, + title: context.strings.signOutFromOtherDevices, + body: context.strings.signOutOtherBody, + isDismissible: false, + firstButtonLabel: context.strings.signOutOtherDevices, + firstButtonType: ButtonType.critical, + firstButtonOnTap: () async { + logOutFromOther = true; + }, + secondButtonLabel: context.strings.doNotSignOut, + secondButtonOnTap: () async { + logOutFromOther = false; + }, + ); + return logOutFromOther; + } + + Future _showRecoveryCodeDialog( + String password, { + bool usingVolatilePassword = false, + }) async { + final dialog = createProgressDialog( + context, context.strings.generatingEncryptionKeysTitle); + await dialog.show(); + try { + if (usingVolatilePassword) { + _logger.info('Using volatile password'); + } + final result = await widget.config.generateKey(password); + widget.config.resetVolatilePassword(); + await dialog.hide(); + onDone() async { + final dialog = + createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + await UserService.instance.setAttributes(result); + await dialog.hide(); + widget.config.resetVolatilePassword(); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return widget.homePage; + }, + ), + (route) => route.isFirst, + ); + } catch (e, s) { + _logger.severe(e, s); + await dialog.hide(); + // ignore: unawaited_futures + showGenericErrorDialog( + context: context, + error: e, + ); + } + } + + // ignore: unawaited_futures + routeToPage( + context, + RecoveryKeyPage( + widget.config, + result.privateKeyAttributes.recoveryKey, + context.strings.continueLabel, + showAppBar: false, + isDismissible: false, + onDone: onDone, + showProgressBar: true, + ), + ); + } catch (e) { + _logger.severe(e); + await dialog.hide(); + if (e is UnsupportedError) { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.insecureDevice, + context.strings.sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease, + ); + } else { + // ignore: unawaited_futures + showGenericErrorDialog( + context: context, + error: e, + ); + } + } + } +} diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart new file mode 100644 index 0000000000..0de41c1992 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -0,0 +1,332 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_accounts/models/errors.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/pages/base_home_page.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:flutter/material.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:logging/logging.dart'; + +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; + +class PasswordReentryPage extends StatefulWidget { + final BaseHomePage homePage; + final BaseConfiguration config; + + const PasswordReentryPage(this.homePage, this.config, {super.key}); + + @override + State createState() => _PasswordReentryPageState(); +} + +class _PasswordReentryPageState extends State { + final _logger = Logger((_PasswordReentryPageState).toString()); + final _passwordController = TextEditingController(); + final FocusNode _passwordFocusNode = FocusNode(); + String? email; + bool _passwordInFocus = false; + bool _passwordVisible = false; + String? _volatilePassword; + + @override + void initState() { + super.initState(); + email = widget.config.getEmail(); + _volatilePassword = widget.config.getVolatilePassword(); + if (_volatilePassword != null) { + _passwordController.text = _volatilePassword!; + Future.delayed( + Duration.zero, + () => verifyPassword(_volatilePassword!, usingVolatilePassword: true), + ); + } + _passwordFocusNode.addListener(() { + setState(() { + _passwordInFocus = _passwordFocusNode.hasFocus; + }); + }); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: _getBody(), + floatingActionButton: DynamicFAB( + key: const ValueKey("verifyPasswordButton"), + isKeypadOpen: isKeypadOpen, + isFormValid: _passwordController.text.isNotEmpty, + buttonText: context.strings.verifyPassword, + onPressedFunction: () async { + FocusScope.of(context).unfocus(); + await verifyPassword(_passwordController.text); + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Future verifyPassword( + String password, { + bool usingVolatilePassword = false, + }) async { + FocusScope.of(context).unfocus(); + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + if (usingVolatilePassword) { + _logger.info("Using volatile password"); + } + try { + final kek = await widget.config.decryptSecretsAndGetKeyEncKey( + password, + widget.config.getKeyAttributes()!, + ); + _registerSRPForExistingUsers(kek).ignore(); + } on KeyDerivationError catch (e, s) { + _logger.severe("Password verification failed", e, s); + await dialog.hide(); + final dialogChoice = await showChoiceDialog( + context, + title: context.strings.recreatePasswordTitle, + body: context.strings.recreatePasswordBody, + firstButtonLabel: context.strings.useRecoveryKey, + ); + if (dialogChoice!.action == ButtonAction.first) { + // ignore: unawaited_futures + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return RecoveryPage(widget.homePage, widget.config); + }, + ), + ); + } + return; + } catch (e, s) { + _logger.severe("Password verification failed", e, s); + await dialog.hide(); + final dialogChoice = await showChoiceDialog( + context, + title: context.strings.incorrectPasswordTitle, + body: context.strings.pleaseTryAgain, + firstButtonLabel: context.strings.contactSupport, + secondButtonLabel: context.strings.ok, + ); + if (dialogChoice!.action == ButtonAction.first) { + await sendLogs( + context, + context.strings.contactSupport, + postShare: () {}, + ); + } + return; + } + widget.config.resetVolatilePassword(); + await dialog.hide(); + unawaited( + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return widget.homePage; + }, + ), + (route) => false, + ), + ); + } + + Future _registerSRPForExistingUsers(Uint8List key) async { + bool shouldSetupSRP = false; + try { + // ignore: unused_local_variable + final attr = await UserService.instance.getSrpAttributes(email!); + } on SrpSetupNotCompleteError { + shouldSetupSRP = true; + } catch (e, s) { + _logger.severe("error while fetching attr", e, s); + } + if (shouldSetupSRP) { + try { + final Uint8List loginKey = await CryptoUtil.deriveLoginKey(key); + await UserService.instance.registerOrUpdateSrp(loginKey); + } catch (e, s) { + _logger.severe("error while setting up srp for existing users", e, s); + } + } + } + + Widget _getBody() { + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: ListView( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Text( + context.strings.welcomeBack, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Visibility( + // hidden textForm for suggesting auto-fill service for saving + // password + visible: false, + child: TextFormField( + autofillHints: const [ + AutofillHints.email, + ], + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: email, + textInputAction: TextInputAction.next, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), + child: TextFormField( + key: const ValueKey("passwordInputField"), + autofillHints: const [AutofillHints.password], + decoration: InputDecoration( + hintText: context.strings.enterYourPasswordHint, + filled: true, + contentPadding: const EdgeInsets.all(20), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _passwordInFocus + ? IconButton( + icon: Icon( + _passwordVisible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ) + : null, + ), + style: const TextStyle( + fontSize: 14, + ), + controller: _passwordController, + autofocus: true, + autocorrect: false, + obscureText: !_passwordVisible, + keyboardType: TextInputType.visiblePassword, + focusNode: _passwordFocusNode, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 18), + child: Divider( + thickness: 1, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return RecoveryPage( + widget.homePage, + widget.config, + ); + }, + ), + ); + }, + child: Center( + child: Text( + context.strings.forgotPassword, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () async { + final dialog = createProgressDialog( + context, + context.strings.pleaseWait, + ); + await dialog.show(); + await widget.config.logout(); + await dialog.hide(); + Navigator.of(context) + .popUntil((route) => route.isFirst); + }, + child: Center( + child: Text( + context.strings.changeEmail, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart new file mode 100644 index 0000000000..20f49b8f52 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -0,0 +1,364 @@ +import 'dart:convert'; +import 'dart:io' as io; + +import 'package:bip39/bip39.dart' as bip39; +import 'package:dotted_border/dotted_border.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_configuration/constants.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_utils/platform_util.dart'; +import 'package:ente_utils/share_utils.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:file_saver/file_saver.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:step_progress_indicator/step_progress_indicator.dart'; + +class RecoveryKeyPage extends StatefulWidget { + final BaseConfiguration config; + final bool? showAppBar; + final String recoveryKey; + final String doneText; + final Function()? onDone; + final bool? isDismissible; + final String? title; + final String? text; + final String? subText; + final bool showProgressBar; + + const RecoveryKeyPage( + this.config, + this.recoveryKey, + this.doneText, { + super.key, + this.showAppBar, + this.onDone, + this.isDismissible, + this.title, + this.text, + this.subText, + this.showProgressBar = false, + }); + + @override + State createState() => _RecoveryKeyPageState(); +} + +class _RecoveryKeyPageState extends State { + bool _hasTriedToSave = false; + late final io.File _recoveryKeyFile; + + @override + void initState() { + super.initState(); + _recoveryKeyFile = io.File( + "${widget.config.getTempDirectory()}ente-recovery-key.txt", + ); + } + + @override + Widget build(BuildContext context) { + final String recoveryKey = bip39.entropyToMnemonic(widget.recoveryKey); + if (recoveryKey.split(' ').length != mnemonicKeyWordCount) { + throw AssertionError( + 'recovery code should have $mnemonicKeyWordCount words', + ); + } + final double topPadding = widget.showAppBar! + ? 40 + : widget.showProgressBar + ? 32 + : 120; + + Future copy() async { + await Clipboard.setData( + ClipboardData( + text: recoveryKey, + ), + ); + showShortToast( + context, + context.strings.recoveryKeyCopiedToClipboard, + ); + setState(() { + _hasTriedToSave = true; + }); + } + + return Scaffold( + appBar: widget.showProgressBar + ? AppBar( + automaticallyImplyLeading: false, + elevation: 0, + title: Hero( + tag: "recovery_key", + child: StepProgressIndicator( + totalSteps: 4, + currentStep: 3, + selectedColor: getEnteColorScheme(context).alternativeColor, + roundedEdges: const Radius.circular(10), + unselectedColor: + getEnteColorScheme(context).stepProgressUnselectedColor, + ), + ), + ) + : widget.showAppBar! + ? AppBar( + elevation: 0, + title: Text(widget.title ?? context.strings.recoveryKey), + ) + : null, + body: Padding( + padding: EdgeInsets.fromLTRB(20, topPadding, 20, 20), + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: constraints.maxWidth, + minHeight: constraints.maxHeight, + ), + child: IntrinsicHeight( + child: Column( + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + widget.showAppBar! + ? const SizedBox.shrink() + : Text( + widget.title ?? context.strings.recoveryKey, + style: Theme.of(context).textTheme.headlineMedium, + ), + Padding( + padding: EdgeInsets.all(widget.showAppBar! ? 0 : 12), + ), + Text( + widget.text ?? + context.strings.recoveryKeyOnForgotPassword, + style: Theme.of(context).textTheme.titleMedium, + ), + const Padding(padding: EdgeInsets.only(top: 24)), + Container( + padding: const EdgeInsets.all(1), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + getEnteColorScheme(context).primary500, + getEnteColorScheme(context).primary300, + ], + stops: [0.0, 0.9753], + ), + ), + child: DottedBorder( + options: const RoundedRectDottedBorderOptions( + padding: EdgeInsets.zero, + strokeWidth: 1, + color: Color(0xFF6B6B6B), + dashPattern: [6, 6], + radius: Radius.circular(8), + ), + child: SizedBox( + width: double.infinity, + child: Stack( + children: [ + Column( + children: [ + Builder( + builder: (context) { + final content = Container( + padding: const EdgeInsets.all(20), + width: double.infinity, + child: Text( + recoveryKey, + textAlign: TextAlign.justify, + style: Theme.of(context) + .textTheme + .bodyLarge, + ), + ); + + if (PlatformUtil.isMobile()) { + return GestureDetector( + onTap: () async => await copy(), + child: content, + ); + } else { + return SelectableRegion( + focusNode: FocusNode(), + selectionControls: + PlatformUtil.selectionControls, + child: content, + ); + } + }, + ), + ], + ), + Positioned( + right: 0, + top: 0, + child: PlatformCopy( + onPressed: copy, + ), + ), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 20), + child: Text( + widget.subText ?? + context.strings.recoveryKeySaveDescription, + style: Theme.of(context).textTheme.bodyLarge, + ), + ), + Expanded( + child: Container( + alignment: Alignment.bottomCenter, + width: double.infinity, + padding: const EdgeInsets.fromLTRB(10, 10, 10, 42), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: _saveOptions(context, recoveryKey), + ), + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ); + } + + List _saveOptions(BuildContext context, String recoveryKey) { + final List childrens = []; + if (!_hasTriedToSave) { + childrens.add( + SizedBox( + height: 56, + child: OutlinedButton( + onPressed: () async { + await _saveKeys(); + }, + child: Text(context.strings.doThisLater), + ), + ), + ); + childrens.add(const SizedBox(height: 10)); + } + + childrens.add( + GradientButton( + onTap: () async { + await shareDialog( + context, + context.strings.recoveryKey, + saveAction: () async { + await _saveRecoveryKey(recoveryKey); + }, + sendAction: () async { + await _shareRecoveryKey(recoveryKey); + }, + ); + }, + text: context.strings.saveKey, + ), + ); + + if (_hasTriedToSave) { + childrens.add(const SizedBox(height: 10)); + childrens.add( + SizedBox( + height: 56, + child: ElevatedButton( + child: Text(widget.doneText), + onPressed: () async { + await _saveKeys(); + }, + ), + ), + ); + } + childrens.add(const SizedBox(height: 12)); + return childrens; + } + + Future _saveRecoveryKey(String recoveryKey) async { + final bytes = utf8.encode(recoveryKey); + final time = DateTime.now().millisecondsSinceEpoch; + + await PlatformUtil.shareFile( + "ente_recovery_key_$time", + "txt", + bytes, + MimeType.text, + ); + + if (mounted) { + showToast( + context, + context.strings.recoveryKeySaved, + ); + setState(() { + _hasTriedToSave = true; + }); + } + } + + Future _shareRecoveryKey(String recoveryKey) async { + if (_recoveryKeyFile.existsSync()) { + await _recoveryKeyFile.delete(); + } + _recoveryKeyFile.writeAsStringSync(recoveryKey); + await Share.shareXFiles([XFile(_recoveryKeyFile.path)]); + Future.delayed(const Duration(milliseconds: 500), () { + if (mounted) { + setState(() { + _hasTriedToSave = true; + }); + } + }); + } + + Future _saveKeys() async { + Navigator.of(context).pop(); + if (_recoveryKeyFile.existsSync()) { + await _recoveryKeyFile.delete(); + } + widget.onDone!(); + } +} + +class PlatformCopy extends StatelessWidget { + const PlatformCopy({ + super.key, + required this.onPressed, + }); + + final void Function() onPressed; + + @override + Widget build(BuildContext context) { + return IconButton( + onPressed: () => onPressed(), + visualDensity: VisualDensity.compact, + icon: const Icon( + Icons.copy, + size: 16, + ), + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart new file mode 100644 index 0000000000..0901452a11 --- /dev/null +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -0,0 +1,167 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/buttons/dynamic_fab.dart'; +import 'package:ente_ui/pages/base_home_page.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; + +class RecoveryPage extends StatefulWidget { + final BaseHomePage homePage; + final BaseConfiguration config; + + const RecoveryPage(this.homePage, this.config, {super.key}); + + @override + State createState() => _RecoveryPageState(); +} + +class _RecoveryPageState extends State { + final _recoveryKey = TextEditingController(); + + Future onPressed() async { + FocusScope.of(context).unfocus(); + final dialog = createProgressDialog(context, "Decrypting..."); + await dialog.show(); + try { + await widget.config.recover(_recoveryKey.text.trim()); + await dialog.hide(); + showToast(context, "Recovery successful!"); + await Navigator.of(context).pushReplacement( + MaterialPageRoute( + builder: (BuildContext context) { + return PopScope( + canPop: false, + child: PasswordEntryPage( + widget.config, + PasswordEntryMode.reset, + widget.homePage, + ), + ); + }, + ), + ); + } catch (e) { + await dialog.hide(); + String errMessage = 'The recovery key you entered is incorrect'; + if (e is AssertionError) { + errMessage = '$errMessage : ${e.message}'; + } + await showErrorDialog(context, "Incorrect recovery key", errMessage); + } + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + floatingActionButton: DynamicFAB( + isKeypadOpen: isKeypadOpen, + isFormValid: _recoveryKey.text.isNotEmpty, + buttonText: 'Recover', + onPressedFunction: onPressed, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + body: Column( + children: [ + Expanded( + child: ListView( + children: [ + Padding( + padding: + const EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Text( + context.strings.forgotPassword, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), + child: TextFormField( + decoration: InputDecoration( + filled: true, + hintText: "Enter your recovery key", + contentPadding: const EdgeInsets.all(20), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + ), + style: const TextStyle( + fontSize: 14, + fontFeatures: [FontFeature.tabularFigures()], + ), + controller: _recoveryKey, + autofocus: false, + autocorrect: false, + keyboardType: TextInputType.multiline, + maxLines: null, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 18), + child: Divider( + thickness: 1, + ), + ), + Row( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + showErrorDialog( + context, + "Sorry", + "Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key", + ); + }, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 20), + child: Center( + child: Text( + context.strings.noRecoveryKeyTitle, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontSize: 14, + decoration: TextDecoration.underline, + ), + ), + ), + ), + ), + ], + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart new file mode 100644 index 0000000000..0fa74243ad --- /dev/null +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -0,0 +1,222 @@ +import "dart:convert"; +import "dart:typed_data"; + +import "package:ente_configuration/base_configuration.dart"; +import "package:ente_ui/components/buttons/dynamic_fab.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_ui/utils/dialog_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:flutter/material.dart'; +import "package:logging/logging.dart"; + +typedef OnPasswordVerifiedFn = Future Function(Uint8List bytes); + +class RequestPasswordVerificationPage extends StatefulWidget { + final BaseConfiguration config; + final OnPasswordVerifiedFn onPasswordVerified; + final Function? onPasswordError; + + const RequestPasswordVerificationPage( + this.config, { + super.key, + required this.onPasswordVerified, + this.onPasswordError, + }); + + @override + State createState() => + _RequestPasswordVerificationPageState(); +} + +class _RequestPasswordVerificationPageState + extends State { + final _logger = Logger((_RequestPasswordVerificationPageState).toString()); + final _passwordController = TextEditingController(); + final FocusNode _passwordFocusNode = FocusNode(); + String? email; + bool _passwordInFocus = false; + bool _passwordVisible = false; + + @override + void initState() { + super.initState(); + email = widget.config.getEmail(); + _passwordFocusNode.addListener(() { + setState(() { + _passwordInFocus = _passwordFocusNode.hasFocus; + }); + }); + } + + @override + Widget build(BuildContext context) { + final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; + + FloatingActionButtonLocation? fabLocation() { + if (isKeypadOpen) { + return null; + } else { + return FloatingActionButtonLocation.centerFloat; + } + } + + return Scaffold( + resizeToAvoidBottomInset: isKeypadOpen, + appBar: AppBar( + elevation: 0, + leading: IconButton( + icon: const Icon(Icons.arrow_back), + color: Theme.of(context).iconTheme.color, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ), + body: _getBody(), + floatingActionButton: DynamicFAB( + key: const ValueKey("verifyPasswordButton"), + isKeypadOpen: isKeypadOpen, + isFormValid: _passwordController.text.isNotEmpty, + buttonText: context.strings.verifyPassword, + onPressedFunction: () async { + FocusScope.of(context).unfocus(); + final dialog = + createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final attributes = widget.config.getKeyAttributes()!; + final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey( + utf8.encode(_passwordController.text), + CryptoUtil.base642bin(attributes.kekSalt), + attributes.memLimit, + attributes.opsLimit, + ); + CryptoUtil.decryptSync( + CryptoUtil.base642bin(attributes.encryptedKey), + keyEncryptionKey, + CryptoUtil.base642bin(attributes.keyDecryptionNonce), + ); + await dialog.show(); + // pop + await widget.onPasswordVerified(keyEncryptionKey); + await dialog.hide(); + Navigator.of(context).pop(true); + } catch (e, s) { + _logger.severe("Error while verifying password", e, s); + await dialog.hide(); + if (widget.onPasswordError != null) { + widget.onPasswordError!(); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.incorrectPasswordTitle, + context.strings.pleaseTryAgain, + ); + } + } + }, + ), + floatingActionButtonLocation: fabLocation(), + floatingActionButtonAnimator: NoScalingAnimation(), + ); + } + + Widget _getBody() { + return Column( + children: [ + Expanded( + child: AutofillGroup( + child: ListView( + children: [ + Padding( + padding: const EdgeInsets.only(top: 30, left: 20, right: 20), + child: Text( + context.strings.enterPassword, + style: Theme.of(context).textTheme.headlineMedium, + ), + ), + Padding( + padding: const EdgeInsets.only( + bottom: 30, + left: 22, + right: 20, + ), + child: Text( + email ?? '', + style: getEnteTextTheme(context).smallMuted, + ), + ), + Visibility( + // hidden textForm for suggesting auto-fill service for saving + // password + visible: false, + child: TextFormField( + autofillHints: const [ + AutofillHints.email, + ], + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: email, + textInputAction: TextInputAction.next, + ), + ), + Padding( + padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), + child: TextFormField( + key: const ValueKey("passwordInputField"), + autofillHints: const [AutofillHints.password], + decoration: InputDecoration( + hintText: context.strings.enterYourPasswordHint, + filled: true, + contentPadding: const EdgeInsets.all(20), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(6), + ), + suffixIcon: _passwordInFocus + ? IconButton( + icon: Icon( + _passwordVisible + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context).iconTheme.color, + size: 20, + ), + onPressed: () { + setState(() { + _passwordVisible = !_passwordVisible; + }); + }, + ) + : null, + ), + style: const TextStyle( + fontSize: 14, + ), + controller: _passwordController, + autofocus: true, + autocorrect: false, + obscureText: !_passwordVisible, + keyboardType: TextInputType.visiblePassword, + focusNode: _passwordFocusNode, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Padding( + padding: EdgeInsets.symmetric(vertical: 18), + child: Divider( + thickness: 1, + ), + ), + ], + ), + ), + ), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart new file mode 100644 index 0000000000..29774497ee --- /dev/null +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -0,0 +1,231 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_ui/components/loading_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import 'package:ente_utils/date_time_util.dart'; + +class SessionsPage extends StatefulWidget { + final BaseConfiguration config; + const SessionsPage( + this.config, { + super.key, + }); + + @override + State createState() => _SessionsPageState(); +} + +class _SessionsPageState extends State { + Sessions? _sessions; + final Logger _logger = Logger("SessionsPageState"); + + @override + void initState() { + _fetchActiveSessions().onError((error, stackTrace) { + showToast(context, "Failed to fetch active sessions"); + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + title: Text(context.strings.activeSessions), + ), + body: _getBody(), + ); + } + + Widget _getBody() { + if (_sessions == null) { + return const Center(child: EnteLoadingWidget()); + } + final List rows = []; + rows.add(const Padding(padding: EdgeInsets.all(4))); + for (final session in _sessions!.sessions) { + rows.add(_getSessionWidget(session)); + } + return SingleChildScrollView( + child: Column( + children: rows, + ), + ); + } + + Widget _getSessionWidget(Session session) { + final lastUsedTime = + DateTime.fromMicrosecondsSinceEpoch(session.lastUsedTime); + return Column( + children: [ + InkWell( + onTap: () async { + _showSessionTerminationDialog(session); + }, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _getUAWidget(session), + const Padding(padding: EdgeInsets.all(4)), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + child: Text( + session.ip, + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.8), + fontSize: 14, + ), + ), + ), + const Padding(padding: EdgeInsets.all(8)), + Flexible( + child: Text( + getFormattedTime(lastUsedTime), + style: TextStyle( + color: Theme.of(context) + .colorScheme + .onSurface + .withOpacity(0.8), + fontSize: 12, + ), + ), + ), + ], + ), + ], + ), + ), + ), + const Divider(), + ], + ); + } + + Future _terminateSession(Session session) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + await UserService.instance.terminateSession(session.token); + await _fetchActiveSessions(); + await dialog.hide(); + } catch (e) { + await dialog.hide(); + _logger.severe('failed to terminate'); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.somethingWentWrongPleaseTryAgain, + ); + } + } + + Future _fetchActiveSessions() async { + _sessions = await UserService.instance.getActiveSessions().onError((e, s) { + _logger.severe("failed to fetch active sessions", e, s); + throw e!; + }); + if (_sessions != null) { + _sessions!.sessions.sort((first, second) { + return second.lastUsedTime.compareTo(first.lastUsedTime); + }); + if (mounted) { + setState(() {}); + } + } + } + + void _showSessionTerminationDialog(Session session) { + final isLoggingOutFromThisDevice = + session.token == widget.config.getToken(); + Widget text; + if (isLoggingOutFromThisDevice) { + text = Text( + context.strings.thisWillLogYouOutOfThisDevice, + ); + } else { + text = SingleChildScrollView( + child: Column( + children: [ + Text( + context.strings.thisWillLogYouOutOfTheFollowingDevice, + ), + const Padding(padding: EdgeInsets.all(8)), + Text( + session.ua, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ), + ); + } + final AlertDialog alert = AlertDialog( + title: Text(context.strings.terminateSession), + content: text, + actions: [ + TextButton( + child: Text( + context.strings.terminate, + style: const TextStyle( + color: Colors.red, + ), + ), + onPressed: () async { + Navigator.of(context, rootNavigator: true).pop('dialog'); + if (isLoggingOutFromThisDevice) { + await UserService.instance.logout(context); + } else { + await _terminateSession(session); + } + }, + ), + TextButton( + child: Text( + context.strings.cancel, + style: TextStyle( + color: isLoggingOutFromThisDevice + ? getEnteColorScheme(context).alternativeColor + : getEnteColorScheme(context).textBase, + ), + ), + onPressed: () { + Navigator.of(context, rootNavigator: true).pop('dialog'); + }, + ), + ], + ); + + showDialog( + context: context, + builder: (BuildContext context) { + return alert; + }, + ); + } + + Widget _getUAWidget(Session session) { + if (session.token == widget.config.getToken()) { + return Text( + context.strings.thisDevice, + style: TextStyle( + fontWeight: FontWeight.bold, + color: getEnteColorScheme(context).alternativeColor, + ), + ); + } + return Text(session.prettyUA); + } +} diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart new file mode 100644 index 0000000000..58a7312e5a --- /dev/null +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -0,0 +1,163 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:pinput/pinput.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; + +class TwoFactorAuthenticationPage extends StatefulWidget { + final String sessionID; + + const TwoFactorAuthenticationPage(this.sessionID, {super.key}); + + @override + State createState() => + _TwoFactorAuthenticationPageState(); +} + +class _TwoFactorAuthenticationPageState + extends State { + final _pinController = TextEditingController(); + String _code = ""; + late LifecycleEventHandler _lifecycleEventHandler; + + @override + void initState() { + _lifecycleEventHandler = LifecycleEventHandler( + resumeCallBack: () async { + if (mounted) { + final data = await Clipboard.getData(Clipboard.kTextPlain); + if (data != null && data.text != null && data.text!.length == 6) { + _pinController.text = data.text!; + } + } + }, + ); + WidgetsBinding.instance.addObserver(_lifecycleEventHandler); + super.initState(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(_lifecycleEventHandler); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + + final pinPutDecoration = PinTheme( + height: 45, + width: 45, + decoration: BoxDecoration( + border: Border.all(color: colorScheme.primary500), + borderRadius: BorderRadius.circular(15.0), + ), + ); + + return Scaffold( + appBar: AppBar( + title: Text( + context.strings.twoFactorAuthTitle, + ), + ), + body: _getBody(pinPutDecoration), + ); + } + + Widget _getBody(PinTheme pinPutDecoration) { + final colorScheme = getEnteColorScheme(context); + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Text( + context.strings.enterCodeHint, + style: const TextStyle( + height: 1.4, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + const Padding(padding: EdgeInsets.all(32)), + Padding( + padding: const EdgeInsets.fromLTRB(40, 0, 40, 0), + child: Pinput( + length: 6, + onCompleted: (String code) { + _verifyTwoFactorCode(code); + }, + onChanged: (String pin) { + setState(() { + _code = pin; + }); + }, + controller: _pinController, + submittedPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.0), + border: Border.all( + color: colorScheme.primary500.withOpacity(0.5), + ), + ), + ), + defaultPinTheme: pinPutDecoration, + followingPinTheme: pinPutDecoration.copyWith( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10.0), + border: Border.all( + color: colorScheme.primary500.withOpacity(0.5), + ), + ), + ), + autofocus: true, + ), + ), + const Padding(padding: EdgeInsets.all(24)), + Container( + padding: const EdgeInsets.fromLTRB(80, 0, 80, 0), + width: double.infinity, + height: 64, + child: OutlinedButton( + onPressed: _code.length == 6 + ? () async { + await _verifyTwoFactorCode(_code); + } + : null, + child: Text(context.strings.verify), + ), + ), + const Padding(padding: EdgeInsets.all(30)), + GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + UserService.instance.recoverTwoFactor( + context, + widget.sessionID, + TwoFactorType.totp, + ); + }, + child: Container( + padding: const EdgeInsets.all(10), + child: Center( + child: Text( + context.strings.lostDeviceTitle, + style: const TextStyle( + decoration: TextDecoration.underline, + fontSize: 12, + ), + ), + ), + ), + ), + ], + ); + } + + Future _verifyTwoFactorCode(String code) async { + await UserService.instance.verifyTwoFactor(context, widget.sessionID, code); + } +} diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart new file mode 100644 index 0000000000..12c6da59aa --- /dev/null +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -0,0 +1,110 @@ +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; + +class TwoFactorRecoveryPage extends StatefulWidget { + final String sessionID; + final String encryptedSecret; + final String secretDecryptionNonce; + final TwoFactorType type; + + const TwoFactorRecoveryPage( + this.type, + this.sessionID, + this.encryptedSecret, + this.secretDecryptionNonce, { + super.key, + }); + + @override + State createState() => _TwoFactorRecoveryPageState(); +} + +class _TwoFactorRecoveryPageState extends State { + final _recoveryKey = TextEditingController(); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + context.strings.recoverAccount, + style: const TextStyle( + fontSize: 18, + ), + ), + ), + body: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + Padding( + padding: const EdgeInsets.fromLTRB(60, 0, 60, 0), + child: TextFormField( + decoration: InputDecoration( + hintText: context.strings.enterRecoveryKeyHint, + contentPadding: const EdgeInsets.all(20), + ), + style: const TextStyle( + fontSize: 14, + fontFeatures: [FontFeature.tabularFigures()], + ), + controller: _recoveryKey, + autofocus: false, + autocorrect: false, + keyboardType: TextInputType.multiline, + maxLines: null, + onChanged: (_) { + setState(() {}); + }, + ), + ), + const Padding(padding: EdgeInsets.all(24)), + Container( + padding: const EdgeInsets.fromLTRB(80, 0, 80, 0), + width: double.infinity, + height: 64, + child: OutlinedButton( + onPressed: _recoveryKey.text.isNotEmpty + ? () async { + await UserService.instance.removeTwoFactor( + context, + widget.type, + widget.sessionID, + _recoveryKey.text, + widget.encryptedSecret, + widget.secretDecryptionNonce, + ); + } + : null, + child: Text(context.strings.recover), + ), + ), + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () async { + await openSupportPage(null, null); + }, + child: Container( + padding: const EdgeInsets.all(40), + child: Center( + child: Text( + context.strings.noRecoveryKeyTitle, + style: TextStyle( + decoration: TextDecoration.underline, + fontSize: 12, + color: + getEnteColorScheme(context).textBase.withOpacity(0.9), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/mobile/packages/accounts/lib/services/passkey_service.dart b/mobile/packages/accounts/lib/services/passkey_service.dart new file mode 100644 index 0000000000..427272a743 --- /dev/null +++ b/mobile/packages/accounts/lib/services/passkey_service.dart @@ -0,0 +1,57 @@ +import 'package:ente_accounts/services/user_service.dart'; +import 'package:ente_network/network.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/widgets.dart'; +import 'package:logging/logging.dart'; +import 'package:url_launcher/url_launcher_string.dart'; + +class PasskeyService { + PasskeyService._privateConstructor(); + static final PasskeyService instance = PasskeyService._privateConstructor(); + + final _enteDio = Network.instance.enteDio; + + Future getAccountsUrl() async { + final response = await _enteDio.get( + "/users/accounts-token", + ); + final accountsUrl = response.data!["accountsUrl"] ?? kAccountsUrl; + final jwtToken = response.data!["accountsToken"] as String; + return "$accountsUrl/passkeys?token=$jwtToken"; + } + + Future isPasskeyRecoveryEnabled() async { + final response = await _enteDio.get( + "/users/two-factor/recovery-status", + ); + return response.data!["isPasskeyRecoveryEnabled"] as bool; + } + + Future configurePasskeyRecovery( + String secret, + String userEncryptedSecret, + String userSecretNonce, + ) async { + await _enteDio.post( + "/users/two-factor/passkeys/configure-recovery", + data: { + "secret": secret, + "userSecretCipher": userEncryptedSecret, + "userSecretNonce": userSecretNonce, + }, + ); + } + + Future openPasskeyPage(BuildContext context) async { + try { + final url = await getAccountsUrl(); + await launchUrlString( + url, + mode: LaunchMode.externalApplication, + ); + } catch (e) { + Logger('PasskeyService').severe("failed to open passkey page", e); + showGenericErrorDialog(context: context, error: e).ignore(); + } + } +} diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart new file mode 100644 index 0000000000..8af3d55973 --- /dev/null +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -0,0 +1,1091 @@ +import 'dart:async'; +import "dart:convert"; +import "dart:math"; + +import 'package:bip39/bip39.dart' as bip39; +import 'package:dio/dio.dart'; +import 'package:ente_accounts/models/delete_account.dart'; +import 'package:ente_accounts/models/errors.dart'; +import 'package:ente_accounts/models/sessions.dart'; +import 'package:ente_accounts/models/set_keys_request.dart'; +import 'package:ente_accounts/models/set_recovery_key_request.dart'; +import 'package:ente_accounts/models/srp.dart'; +import 'package:ente_accounts/models/two_factor.dart'; +import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/pages/login_page.dart'; +import 'package:ente_accounts/pages/ott_verification_page.dart'; +import 'package:ente_accounts/pages/passkey_page.dart'; +import 'package:ente_accounts/pages/password_entry_page.dart'; +import 'package:ente_accounts/pages/password_reentry_page.dart'; +import 'package:ente_accounts/pages/recovery_page.dart'; +import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; +import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; +import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_configuration/constants.dart'; +import 'package:ente_network/network.dart'; +import 'package:ente_ui/components/progress_dialog.dart'; +import 'package:ente_ui/pages/base_home_page.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import "package:flutter/foundation.dart"; +import 'package:flutter/material.dart'; +import 'package:logging/logging.dart'; +import "package:pointycastle/export.dart"; +import "package:pointycastle/srp/srp6_client.dart"; +import "package:pointycastle/srp/srp6_standard_groups.dart"; +import "package:pointycastle/srp/srp6_util.dart"; +import "package:pointycastle/srp/srp6_verifier_generator.dart"; +import 'package:shared_preferences/shared_preferences.dart'; +import "package:uuid/uuid.dart"; + +const String kAccountsUrl = "https://accounts.ente.io"; + +class UserService { + static const keyHasEnabledTwoFactor = "has_enabled_two_factor"; + static const keyUserDetails = "user_details"; + static const kReferralSource = "referral_source"; + static const kCanDisableEmailMFA = "can_disable_email_mfa"; + static const kIsEmailMFAEnabled = "is_email_mfa_enabled"; + final SRP6GroupParameters kDefaultSrpGroup = SRP6StandardGroups.rfc5054_4096; + final _dio = Network.instance.getDio(); + final _enteDio = Network.instance.enteDio; + final _logger = Logger((UserService).toString()); + late SharedPreferences _preferences; + late ValueNotifier emailValueNotifier; + late BaseConfiguration _config; + late BaseHomePage _homePage; + + UserService._privateConstructor(); + + static final UserService instance = UserService._privateConstructor(); + + Future init(BaseConfiguration config, BaseHomePage homePage) async { + _config = config; + _homePage = homePage; + emailValueNotifier = ValueNotifier(config.getEmail()); + _preferences = await SharedPreferences.getInstance(); + } + + Future sendOtt( + BuildContext context, + String email, { + bool isChangeEmail = false, + bool isCreateAccountScreen = false, + bool isResetPasswordScreen = false, + String? purpose, + }) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final response = await _dio.post( + "${_config.getHttpEndpoint()}/users/ott", + data: { + "email": email, + "purpose": isChangeEmail ? "change" : purpose ?? "", + }, + ); + await dialog.hide(); + if (response.statusCode == 200) { + unawaited( + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return OTTVerificationPage( + email, + isChangeEmail: isChangeEmail, + isCreateAccountScreen: isCreateAccountScreen, + isResetPasswordScreen: isResetPasswordScreen, + ); + }, + ), + ), + ); + return; + } + unawaited(showGenericErrorDialog(context: context, error: null)); + } on DioException catch (e) { + await dialog.hide(); + _logger.info(e); + final String? enteErrCode = e.response?.data["code"]; + if (enteErrCode != null && enteErrCode == "USER_ALREADY_REGISTERED") { + unawaited( + showErrorDialog( + context, + context.strings.oops, + context.strings.emailAlreadyRegistered, + ), + ); + } else if (enteErrCode != null && enteErrCode == "USER_NOT_REGISTERED") { + unawaited( + showErrorDialog( + context, + context.strings.oops, + context.strings.emailNotRegistered, + ), + ); + } else if (e.response != null && e.response!.statusCode == 403) { + unawaited( + showErrorDialog( + context, + context.strings.oops, + context.strings.thisEmailIsAlreadyInUse, + ), + ); + } else { + unawaited(showGenericErrorDialog(context: context, error: e)); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + unawaited(showGenericErrorDialog(context: context, error: e)); + } + } + + Future sendFeedback( + BuildContext context, + String feedback, { + String type = "SubCancellation", + }) async { + await _dio.post( + "${_config.getHttpEndpoint()}/anonymous/feedback", + data: {"feedback": feedback, "type": "type"}, + ); + } + + Future getUserDetailsV2({ + bool memoryCount = false, + bool shouldCache = true, + }) async { + try { + final response = await _enteDio.get( + "/users/details/v2", + queryParameters: { + "memoryCount": memoryCount, + }, + ); + final userDetails = UserDetails.fromMap(response.data); + if (shouldCache) { + if (userDetails.profileData != null) { + await _preferences.setBool( + kIsEmailMFAEnabled, + userDetails.profileData!.isEmailMFAEnabled, + ); + await _preferences.setBool( + kCanDisableEmailMFA, + userDetails.profileData!.canDisableEmailMFA, + ); + } + // handle email change from different client + if (userDetails.email != _config.getEmail()) { + await setEmail(userDetails.email); + } + } + return userDetails; + } catch (e) { + _logger.warning("Failed to fetch", e); + if (e is DioException && e.response?.statusCode == 401) { + throw UnauthorizedError(); + } else { + rethrow; + } + } + } + + UserDetails? getCachedUserDetails() { + if (_preferences.containsKey(keyUserDetails)) { + return UserDetails.fromJson(_preferences.getString(keyUserDetails)!); + } + return null; + } + + Future getActiveSessions() async { + try { + final response = await _enteDio.get("/users/sessions"); + return Sessions.fromMap(response.data); + } on DioException catch (e) { + _logger.info(e); + rethrow; + } + } + + Future terminateSession(String token) async { + try { + await _enteDio.delete( + "/users/session", + queryParameters: { + "token": token, + }, + ); + } on DioException catch (e) { + _logger.info(e); + rethrow; + } + } + + Future leaveFamilyPlan() async { + try { + await _enteDio.delete("/family/leave"); + } on DioException catch (e) { + _logger.warning('failed to leave family plan', e); + rethrow; + } + } + + Future logout(BuildContext context) async { + try { + final response = await _enteDio.post("/users/logout"); + if (response.statusCode == 200) { + await _config.logout(); + Navigator.of(context).popUntil((route) => route.isFirst); + } else { + throw Exception("Log out action failed"); + } + } catch (e) { + _logger.severe(e); + // check if token is already invalid + if (e is DioException && e.response?.statusCode == 401) { + await _config.logout(); + Navigator.of(context).popUntil((route) => route.isFirst); + return; + } + //This future is for waiting for the dialog from which logout() is called + //to close and only then to show the error dialog. + Future.delayed( + const Duration(milliseconds: 150), + () => showGenericErrorDialog(context: context, error: e), + ); + rethrow; + } + } + + Future getDeleteChallenge( + BuildContext context, + ) async { + try { + final response = await _enteDio.get("/users/delete-challenge"); + if (response.statusCode == 200) { + return DeleteChallengeResponse( + allowDelete: response.data["allowDelete"] as bool, + encryptedChallenge: response.data["encryptedChallenge"], + ); + } else { + throw Exception("delete action failed"); + } + } catch (e) { + _logger.severe(e); + await showGenericErrorDialog( + context: context, + error: e, + ); + return null; + } + } + + Future deleteAccount( + BuildContext context, + String challengeResponse, + ) async { + try { + final response = await _enteDio.delete( + "/users/delete", + data: { + "challenge": challengeResponse, + }, + ); + if (response.statusCode == 200) { + // clear data + await _config.logout(); + } else { + throw Exception("delete action failed"); + } + } catch (e) { + _logger.severe(e); + rethrow; + } + } + + Future getTokenForPasskeySession(String sessionID) async { + try { + final response = await _dio.get( + "${_config.getHttpEndpoint()}/users/two-factor/passkeys/get-token", + queryParameters: { + "sessionID": sessionID, + }, + ); + return response.data; + } on DioException catch (e) { + if (e.response != null) { + if (e.response!.statusCode == 404 || e.response!.statusCode == 410) { + throw PassKeySessionExpiredError(); + } + if (e.response!.statusCode == 400) { + throw PassKeySessionNotVerifiedError(); + } + } + rethrow; + } catch (e, s) { + _logger.severe("unexpected error", e, s); + rethrow; + } + } + + Future onPassKeyVerified(BuildContext context, Map response) async { + final ProgressDialog dialog = + createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final userPassword = _config.getVolatilePassword(); + await _saveConfiguration(response); + if (userPassword == null) { + await dialog.hide(); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return PasswordReentryPage(_homePage, _config); + }, + ), + (route) => route.isFirst, + ); + } else { + Widget page; + if (_config.getEncryptedToken() != null) { + await _config.decryptSecretsAndGetKeyEncKey( + userPassword, + _config.getKeyAttributes()!, + ); + _config.resetVolatilePassword(); + page = _homePage; + } else { + throw Exception("unexpected response during passkey verification"); + } + await dialog.hide(); + + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return page; + }, + ), + (route) => route.isFirst, + ); + } + } catch (e) { + _logger.severe(e); + await dialog.hide(); + rethrow; + } + } + + Future verifyEmail( + BuildContext context, + String ott, { + bool isResettingPasswordScreen = false, + }) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + final verifyData = { + "email": _config.getEmail(), + "ott": ott, + }; + if (!_config.isLoggedIn()) { + verifyData["source"] = 'auth:${_getRefSource()}'; + } + try { + final response = await _dio.post( + "${_config.getHttpEndpoint()}/users/verify-email", + data: verifyData, + ); + await dialog.hide(); + if (response.statusCode == 200) { + Widget page; + final String passkeySessionID = response.data["passkeySessionID"]; + final String accountsUrl = response.data["accountsUrl"] ?? kAccountsUrl; + String twoFASessionID = response.data["twoFactorSessionID"]; + if (twoFASessionID.isEmpty && + response.data["twoFactorSessionIDV2"] != null) { + twoFASessionID = response.data["twoFactorSessionIDV2"]; + } + if (passkeySessionID.isNotEmpty) { + page = PasskeyPage( + _config, + passkeySessionID, + totp2FASessionID: twoFASessionID, + accountsUrl: accountsUrl, + ); + } else if (twoFASessionID.isNotEmpty) { + page = TwoFactorAuthenticationPage(twoFASessionID); + } else { + await _saveConfiguration(response); + if (_config.getEncryptedToken() != null) { + if (isResettingPasswordScreen) { + page = RecoveryPage(_homePage, _config); + } else { + page = PasswordReentryPage(_homePage, _config); + } + } else { + page = PasswordEntryPage( + _config, + PasswordEntryMode.set, + _homePage, + ); + } + } + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return page; + }, + ), + (route) => route.isFirst, + ); + } else { + // should never reach here + throw Exception("unexpected response during email verification"); + } + } on DioException catch (e) { + _logger.info(e); + await dialog.hide(); + if (e.response != null && e.response!.statusCode == 410) { + await showErrorDialog( + context, + context.strings.oops, + context.strings.yourVerificationCodeHasExpired, + ); + Navigator.of(context).pop(); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.incorrectCode, + context.strings.sorryTheCodeYouveEnteredIsIncorrect, + ); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.verificationFailedPleaseTryAgain, + ); + } + } + + Future setEmail(String email) async { + await _config.setEmail(email); + emailValueNotifier.value = email; + } + + Future changeEmail( + BuildContext context, + String email, + String ott, + ) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final response = await _enteDio.post( + "/users/change-email", + data: { + "email": email, + "ott": ott, + }, + ); + await dialog.hide(); + if (response.statusCode == 200) { + showShortToast(context, context.strings.emailChangedTo(email)); + await setEmail(email); + Navigator.of(context).popUntil((route) => route.isFirst); + Bus.instance.fire(UserDetailsChangedEvent()); + return; + } + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.verificationFailedPleaseTryAgain, + ); + } on DioException catch (e) { + await dialog.hide(); + if (e.response != null && e.response!.statusCode == 403) { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.thisEmailIsAlreadyInUse, + ); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.incorrectCode, + context.strings.authenticationFailedPleaseTryAgain, + ); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.verificationFailedPleaseTryAgain, + ); + } + } + + Future setAttributes(KeyGenResult result) async { + try { + await registerOrUpdateSrp(result.loginKey); + await _enteDio.put( + "/users/attributes", + data: { + "keyAttributes": result.keyAttributes.toMap(), + }, + ); + await _config.setKey(result.privateKeyAttributes.key); + await _config.setSecretKey(result.privateKeyAttributes.secretKey); + await _config.setKeyAttributes(result.keyAttributes); + } catch (e) { + _logger.severe(e); + rethrow; + } + } + + Future getSrpAttributes(String email) async { + try { + final response = await _dio.get( + "${_config.getHttpEndpoint()}/users/srp/attributes", + queryParameters: { + "email": email, + }, + ); + if (response.statusCode == 200) { + return SrpAttributes.fromMap(response.data); + } else { + throw Exception("get-srp-attributes action failed"); + } + } on DioException catch (e) { + if (e.response != null && e.response!.statusCode == 404) { + throw SrpSetupNotCompleteError(); + } + rethrow; + } catch (e) { + rethrow; + } + } + + Future registerOrUpdateSrp( + Uint8List loginKey, { + SetKeysRequest? setKeysRequest, + bool logOutOtherDevices = false, + }) async { + try { + final String username = const Uuid().v4().toString(); + final SecureRandom random = _getSecureRandom(); + final Uint8List identity = Uint8List.fromList(utf8.encode(username)); + final Uint8List password = loginKey; + final Uint8List salt = random.nextBytes(16); + final gen = SRP6VerifierGenerator( + group: kDefaultSrpGroup, + digest: Digest('SHA-256'), + ); + final v = gen.generateVerifier(salt, identity, password); + + final client = SRP6Client( + group: kDefaultSrpGroup, + digest: Digest('SHA-256'), + random: random, + ); + + final A = client.generateClientCredentials(salt, identity, password); + final request = SetupSRPRequest( + srpUserID: username, + srpSalt: base64Encode(salt), + srpVerifier: base64Encode(SRP6Util.encodeBigInt(v)), + srpA: base64Encode(SRP6Util.encodeBigInt(A!)), + isUpdate: false, + ); + final response = await _enteDio.post( + "/users/srp/setup", + data: request.toMap(), + ); + if (response.statusCode == 200) { + final SetupSRPResponse setupSRPResponse = + SetupSRPResponse.fromJson(response.data); + final serverB = + SRP6Util.decodeBigInt(base64Decode(setupSRPResponse.srpB)); + // ignore: unused_local_variable + final clientS = client.calculateSecret(serverB); + final clientM = client.calculateClientEvidenceMessage(); + + if (setKeysRequest == null) { + await _enteDio.post( + "/users/srp/complete", + data: { + 'setupID': setupSRPResponse.setupID, + 'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)), + }, + ); + } else { + await _enteDio.post( + "/users/srp/update", + data: { + 'setupID': setupSRPResponse.setupID, + 'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)), + 'updatedKeyAttr': setKeysRequest.toMap(), + 'logOutOtherDevices': logOutOtherDevices, + }, + ); + } + } else { + throw Exception("register-srp action failed"); + } + } catch (e, s) { + _logger.severe("failed to register srp", e, s); + rethrow; + } + } + + SecureRandom _getSecureRandom() { + final List seeds = []; + final random = Random.secure(); + for (int i = 0; i < 32; i++) { + seeds.add(random.nextInt(255)); + } + final secureRandom = FortunaRandom(); + secureRandom.seed(KeyParameter(Uint8List.fromList(seeds))); + return secureRandom; + } + + Future verifyEmailViaPassword( + BuildContext context, + SrpAttributes srpAttributes, + String userPassword, + ProgressDialog dialog, + ) async { + late Uint8List keyEncryptionKey; + _logger.finest('Start deriving key'); + keyEncryptionKey = await CryptoUtil.deriveKey( + utf8.encode(userPassword), + CryptoUtil.base642bin(srpAttributes.kekSalt), + srpAttributes.memLimit, + srpAttributes.opsLimit, + ); + _logger.finest('keyDerivation done, derive LoginKey'); + final loginKey = await CryptoUtil.deriveLoginKey(keyEncryptionKey); + final Uint8List identity = Uint8List.fromList( + utf8.encode(srpAttributes.srpUserID), + ); + _logger.finest('longinKey derivation done'); + final Uint8List salt = base64Decode(srpAttributes.srpSalt); + final Uint8List password = loginKey; + final SecureRandom random = _getSecureRandom(); + + final client = SRP6Client( + group: kDefaultSrpGroup, + digest: Digest('SHA-256'), + random: random, + ); + + final A = client.generateClientCredentials(salt, identity, password); + final createSessionResponse = await _dio.post( + "${_config.getHttpEndpoint()}/users/srp/create-session", + data: { + "srpUserID": srpAttributes.srpUserID, + "srpA": base64Encode(SRP6Util.getPadded(A!, 512)), + }, + ); + final String sessionID = createSessionResponse.data["sessionID"]; + final String srpB = createSessionResponse.data["srpB"]; + + final serverB = SRP6Util.decodeBigInt(base64Decode(srpB)); + + // ignore: unused_local_variable + final clientS = client.calculateSecret(serverB); + final clientM = client.calculateClientEvidenceMessage(); + final response = await _dio.post( + "${_config.getHttpEndpoint()}/users/srp/verify-session", + data: { + "sessionID": sessionID, + "srpUserID": srpAttributes.srpUserID, + "srpM1": base64Encode(SRP6Util.getPadded(clientM!, 32)), + }, + ); + if (response.statusCode == 200) { + Widget? page; + final String passkeySessionID = response.data["passkeySessionID"]; + final String accountsUrl = response.data["accountsUrl"] ?? kAccountsUrl; + String twoFASessionID = response.data["twoFactorSessionID"]; + if (twoFASessionID.isEmpty && + response.data["twoFactorSessionIDV2"] != null) { + twoFASessionID = response.data["twoFactorSessionIDV2"]; + } + _config.setVolatilePassword(userPassword); + if (passkeySessionID.isNotEmpty) { + page = PasskeyPage( + _config, + passkeySessionID, + totp2FASessionID: twoFASessionID, + accountsUrl: accountsUrl, + ); + } else if (twoFASessionID.isNotEmpty) { + page = TwoFactorAuthenticationPage(twoFASessionID); + } else { + await _saveConfiguration(response); + if (_config.getEncryptedToken() != null) { + await _config.decryptSecretsAndGetKeyEncKey( + userPassword, + _config.getKeyAttributes()!, + keyEncryptionKey: keyEncryptionKey, + ); + page = _homePage; + } else { + throw Exception("unexpected response during email verification"); + } + } + await dialog.hide(); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return page!; + }, + ), + (route) => route.isFirst, + ); + } else { + // should never reach here + throw Exception("unexpected response during email verification"); + } + } + + Future updateKeyAttributes( + KeyAttributes keyAttributes, + Uint8List loginKey, { + required bool logoutOtherDevices, + }) async { + try { + final setKeyRequest = SetKeysRequest( + kekSalt: keyAttributes.kekSalt, + encryptedKey: keyAttributes.encryptedKey, + keyDecryptionNonce: keyAttributes.keyDecryptionNonce, + memLimit: keyAttributes.memLimit, + opsLimit: keyAttributes.opsLimit, + ); + await registerOrUpdateSrp( + loginKey, + setKeysRequest: setKeyRequest, + logOutOtherDevices: logoutOtherDevices, + ); + await _config.setKeyAttributes(keyAttributes); + } catch (e) { + _logger.severe(e); + rethrow; + } + } + + Future setRecoveryKey(KeyAttributes keyAttributes) async { + try { + final setRecoveryKeyRequest = SetRecoveryKeyRequest( + keyAttributes.masterKeyEncryptedWithRecoveryKey, + keyAttributes.masterKeyDecryptionNonce, + keyAttributes.recoveryKeyEncryptedWithMasterKey, + keyAttributes.recoveryKeyDecryptionNonce, + ); + await _enteDio.put( + "/users/recovery-key", + data: setRecoveryKeyRequest.toMap(), + ); + await _config.setKeyAttributes(keyAttributes); + } catch (e) { + _logger.severe(e); + rethrow; + } + } + + Future verifyTwoFactor( + BuildContext context, + String sessionID, + String code, + ) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final response = await _dio.post( + "${_config.getHttpEndpoint()}/users/two-factor/verify", + data: { + "sessionID": sessionID, + "code": code, + }, + ); + await dialog.hide(); + if (response.statusCode == 200) { + showShortToast(context, context.strings.authenticationSuccessful); + await _saveConfiguration(response); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return PasswordReentryPage(_homePage, _config); + }, + ), + (route) => route.isFirst, + ); + } + } on DioException catch (e) { + await dialog.hide(); + _logger.severe(e); + if (e.response != null && e.response!.statusCode == 404) { + showToast(context, "Session expired"); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginPage(_config); + }, + ), + (route) => route.isFirst, + ); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.incorrectCode, + context.strings.authenticationFailedPleaseTryAgain, + ); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.authenticationFailedPleaseTryAgain, + ); + } + } + + Future recoverTwoFactor( + BuildContext context, + String sessionID, + TwoFactorType type, + ) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + try { + final response = await _dio.get( + "${_config.getHttpEndpoint()}/users/two-factor/recover", + queryParameters: { + "sessionID": sessionID, + "twoFactorType": twoFactorTypeToString(type), + }, + ); + await dialog.hide(); + if (response.statusCode == 200) { + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return TwoFactorRecoveryPage( + type, + sessionID, + response.data["encryptedSecret"], + response.data["secretDecryptionNonce"], + ); + }, + ), + (route) => route.isFirst, + ); + } + } on DioException catch (e) { + await dialog.hide(); + _logger.severe(e); + if (e.response != null && e.response!.statusCode == 404) { + showToast(context, context.strings.sessionExpired); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginPage(_config); + }, + ), + (route) => route.isFirst, + ); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.somethingWentWrongPleaseTryAgain, + ); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.somethingWentWrongPleaseTryAgain, + ); + } finally { + await dialog.hide(); + } + } + + Future removeTwoFactor( + BuildContext context, + TwoFactorType type, + String sessionID, + String recoveryKey, + String encryptedSecret, + String secretDecryptionNonce, + ) async { + final dialog = createProgressDialog(context, context.strings.pleaseWait); + await dialog.show(); + String secret; + try { + if (recoveryKey.contains(' ')) { + if (recoveryKey.split(' ').length != mnemonicKeyWordCount) { + throw AssertionError( + 'recovery code should have $mnemonicKeyWordCount words', + ); + } + recoveryKey = bip39.mnemonicToEntropy(recoveryKey); + } + secret = CryptoUtil.bin2base64( + await CryptoUtil.decrypt( + CryptoUtil.base642bin(encryptedSecret), + CryptoUtil.hex2bin(recoveryKey.trim()), + CryptoUtil.base642bin(secretDecryptionNonce), + ), + ); + } catch (e) { + await dialog.hide(); + await showErrorDialog( + context, + context.strings.incorrectRecoveryKey, + context.strings.theRecoveryKeyYouEnteredIsIncorrect, + ); + return; + } + try { + final response = await _dio.post( + "${_config.getHttpEndpoint()}/users/two-factor/remove", + data: { + "sessionID": sessionID, + "secret": secret, + "twoFactorType": twoFactorTypeToString(type), + }, + ); + await dialog.hide(); + if (response.statusCode == 200) { + showShortToast( + context, + context.strings.twofactorAuthenticationSuccessfullyReset, + ); + await _saveConfiguration(response); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return PasswordReentryPage(_homePage, _config); + }, + ), + (route) => route.isFirst, + ); + } + } on DioException catch (e) { + await dialog.hide(); + _logger.severe(e); + if (e.response != null && e.response!.statusCode == 404) { + showToast(context, "Session expired"); + // ignore: unawaited_futures + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (BuildContext context) { + return LoginPage(_config); + }, + ), + (route) => route.isFirst, + ); + } else { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.somethingWentWrongPleaseTryAgain, + ); + } + } catch (e) { + await dialog.hide(); + _logger.severe(e); + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.oops, + context.strings.somethingWentWrongPleaseTryAgain, + ); + } finally { + await dialog.hide(); + } + } + + Future _saveConfiguration(dynamic response) async { + final responseData = response is Map ? response : response.data as Map?; + if (responseData == null) return; + + await _config.setUserID(responseData["id"]); + if (responseData["encryptedToken"] != null) { + await _config.setEncryptedToken(responseData["encryptedToken"]); + await _config.setKeyAttributes( + KeyAttributes.fromMap(responseData["keyAttributes"]), + ); + } else { + await _config.setToken(responseData["token"]); + } + } + + bool? canDisableEmailMFA() { + return _preferences.getBool(kCanDisableEmailMFA); + } + + bool hasEmailMFAEnabled() { + return _preferences.getBool(kIsEmailMFAEnabled) ?? true; + } + + Future updateEmailMFA(bool isEnabled) async { + try { + await _enteDio.put( + "/users/email-mfa", + data: { + "isEnabled": isEnabled, + }, + ); + await _preferences.setBool(kIsEmailMFAEnabled, isEnabled); + } catch (e) { + _logger.severe("Failed to update email mfa", e); + rethrow; + } + } + + Future setRefSource(String refSource) async { + await _preferences.setString(kReferralSource, refSource); + } + + String _getRefSource() { + return _preferences.getString(kReferralSource) ?? ""; + } +} diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock new file mode 100644 index 0000000000..8fee60bb47 --- /dev/null +++ b/mobile/packages/accounts/pubspec.lock @@ -0,0 +1,1279 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: "direct main" + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: "direct main" + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: df26af0de7c4eff46c53c190b5590e22457bfce6ea679aedb1e6326197f27d6f + url: "https://pub.dev" + source: hosted + version: "1.4.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + dotted_border: + dependency: "direct main" + description: + name: dotted_border + sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + email_validator: + dependency: "direct main" + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_base: + dependency: "direct main" + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_lock_screen: + dependency: "direct main" + description: + path: "../lock_screen" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: transitive + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + ente_network: + dependency: "direct main" + description: + path: "../network" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: "direct main" + description: + path: "../ui" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: "direct main" + description: + path: "../utils" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_saver: + dependency: "direct main" + description: + name: file_saver + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: transitive + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_email_sender: + dependency: transitive + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_local_authentication: + dependency: transitive + description: + name: flutter_local_authentication + sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + jni: + dependency: transitive + description: + name: jni + sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 + url: "https://pub.dev" + source: hosted + version: "0.14.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + local_auth: + dependency: transitive + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + url: "https://pub.dev" + source: hosted + version: "1.0.50" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: transitive + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_dio_adapter: + dependency: transitive + description: + name: native_dio_adapter + sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + password_strength: + dependency: "direct main" + description: + name: password_strength + sha256: "0e51e3d864e37873a1347e658147f88b66e141ee36c58e19828dc5637961e1ce" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: "direct main" + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + privacy_screen: + dependency: transitive + description: + name: privacy_screen + sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + step_progress_indicator: + dependency: "direct main" + description: + name: step_progress_indicator + sha256: b51bb1fcfc78454359f0658c5a2c21548c3825ebf76e826308e9ca10f383bbb8 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + styled_text: + dependency: "direct main" + description: + name: styled_text + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 + url: "https://pub.dev" + source: hosted + version: "8.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + ua_client_hints: + dependency: transitive + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: "direct main" + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xmlstream: + dependency: transitive + description: + name: xmlstream + sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml new file mode 100644 index 0000000000..eca03a2a01 --- /dev/null +++ b/mobile/packages/accounts/pubspec.yaml @@ -0,0 +1,62 @@ +name: ente_accounts +description: A Flutter package containing account-related models, pages, and services for Ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + # Ente packages + ente_strings: + path: ../strings + ente_ui: + path: ../ui + ente_utils: + path: ../utils + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_network: + path: ../network + ente_events: + path: ../events + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_lock_screen: + path: ../lock_screen + + # Third-party dependencies + bip39: ^1.0.6 + dio: ^5.4.0 + logging: ^1.2.0 + pointycastle: ^3.7.3 + shared_preferences: ^2.2.2 + file_saver: ^0.3.0 + share_plus: ^11.0.0 + uuid: ^4.2.1 + collection: ^1.18.0 + dotted_border: ^3.1.0 + password_strength: ^0.2.0 + step_progress_indicator: ^1.0.2 + styled_text: ^8.1.0 + email_validator: ^3.0.0 + pinput: ^5.0.1 + app_links: ^6.3.3 + url_launcher: ^6.3.1 + url_launcher_ios: ^6.3.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + +# This package is not meant to be published +publish_to: none From 28a43393f9438aec7696930481c0647d67624e8c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:14:45 +0530 Subject: [PATCH 036/164] Fix dependency --- mobile/packages/lock_screen/pubspec.lock | 9 +++++---- mobile/packages/lock_screen/pubspec.yaml | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index 29bd04fca3..adae66b006 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -409,10 +409,11 @@ packages: flutter_local_authentication: dependency: "direct main" description: - name: flutter_local_authentication - sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd - url: "https://pub.dev" - source: hosted + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index 02b553a96f..e5906cfc6a 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -26,7 +26,10 @@ dependencies: ente_utils: path: ../utils flutter_animate: ^4.1.0 - flutter_local_authentication: ^1.1.11 + flutter_local_authentication: + git: + url: https://github.com/eaceto/flutter_local_authentication + ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 flutter_secure_storage: ^9.0.0 local_auth: ^2.1.8 logging: ^1.1.1 From 12b0618149a4ecd74ac2053fdb21f2e43a4e3c0a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:21:00 +0530 Subject: [PATCH 037/164] Update strings --- .../1bb2a21ff13ffb469b0dfcac2f14d315/.filecache | 2 +- .../strings/lib/l10n/strings_localizations.dart | 12 ------------ .../strings/lib/l10n/strings_localizations_ar.dart | 7 ------- .../strings/lib/l10n/strings_localizations_bg.dart | 7 ------- .../strings/lib/l10n/strings_localizations_cs.dart | 7 ------- .../strings/lib/l10n/strings_localizations_da.dart | 7 ------- .../strings/lib/l10n/strings_localizations_el.dart | 7 ------- .../strings/lib/l10n/strings_localizations_en.dart | 7 ------- .../strings/lib/l10n/strings_localizations_es.dart | 7 ------- .../strings/lib/l10n/strings_localizations_fr.dart | 7 ------- .../strings/lib/l10n/strings_localizations_id.dart | 7 ------- .../strings/lib/l10n/strings_localizations_ja.dart | 7 ------- .../strings/lib/l10n/strings_localizations_ko.dart | 7 ------- .../strings/lib/l10n/strings_localizations_lt.dart | 7 ------- .../strings/lib/l10n/strings_localizations_nl.dart | 7 ------- .../strings/lib/l10n/strings_localizations_pl.dart | 7 ------- .../strings/lib/l10n/strings_localizations_pt.dart | 7 ------- .../strings/lib/l10n/strings_localizations_ru.dart | 7 ------- .../strings/lib/l10n/strings_localizations_sk.dart | 7 ------- .../strings/lib/l10n/strings_localizations_sr.dart | 7 ------- .../strings/lib/l10n/strings_localizations_sv.dart | 7 ------- .../strings/lib/l10n/strings_localizations_tr.dart | 7 ------- .../strings/lib/l10n/strings_localizations_vi.dart | 7 ------- .../strings/lib/l10n/strings_localizations_zh.dart | 7 ------- 24 files changed, 1 insertion(+), 167 deletions(-) diff --git a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache index 15c66e8975..956c754b17 100644 --- a/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache +++ b/mobile/packages/strings/build/1bb2a21ff13ffb469b0dfcac2f14d315/.filecache @@ -1 +1 @@ -{"version":2,"files":[{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","hash":"8f41b63cc709ff8a753e278d4b7a72ae"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"45c460142089a4ea87608b06ff2628b1"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"25dad41ccf2f92d2c497c7800a1ce45c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","hash":"47d6f18fbae448932b49ce2e88a28702"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","hash":"02b0970a33fd94fa1c05af7bb85eb738"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"4f2fe3f8c87899e023e72b5a170061aa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","hash":"8ffd9cff02f848b7dc5e7ccfcb708118"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","hash":"95baffe47d0162033820e1b0347d2a31"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"09e4f926e05cee727e7f9a32cc98b812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","hash":"4382358f933232e0f06720e23c9bc25d"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"e6e5c63b6ca25a0edba29ef2d7caa0f2"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"5a8a246b080e3003cf8f728d2b10b6f9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"24b4e737f1bef65c324059937e559819"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","hash":"1a4e610bf7483c7de90861aec19afce6"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","hash":"f20b14bffe0344d422010d2b83dca0e0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","hash":"0650c239ebf009771c083ad3b131f800"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","hash":"d240b906f482cfe6867c20dd43955650"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","hash":"4956bbcd9300d54327d95a25fb8c9ac0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart","hash":"0bffb4fff11fee135c7d2178da114719"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","hash":"e52d8d8060197d26a6c99b84ad35a206"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"68c77aa51f2ba7ce1cc24a7ed22a648b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"ee2dfa2a8e23ad82d27e32d499083189"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"ebfcd08d4c861cc0c5d2d5371e430e56"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","hash":"593b91ba1704f1c26aff594bfb8c80ca"},{"path":"/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"5ff2d90544438cc086555a06fe71334b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","hash":"7f1fe83b9f37bd55ea049a6a2e5d1abe"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","hash":"6f559a5d6e765b72ffe412c2b9dde308"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"f5e6b9da5f1fce53cb7a13ed0ac928d4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","hash":"5023855b36d4c402d0a54f5a07ff4a75"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"72a20184b7c14aa3bebc1829168ec249"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"b8b1ab1d8d851e1efdaac119ca9aed51"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","hash":"eeccfcc22b8df214f18d4bdd720a6aa4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","hash":"15949a7f7d740883d50afaffed9723b0"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"a8421a85bad5a6adbff705117f683290"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","hash":"5cb929b88010b3d89b5e9c9e717fe771"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"05da370179c31437bac685fae12ba5e3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"faab6eda68e6fc4782603410686f468a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"b344d11d93750b02904d1ee4b1e40d9e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","hash":"28c967ee98f78d3372480386856345e9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"55ad5a67f7709ca095b64d46c68feaf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","hash":"fb6ab9b68ee2b2dbc9c5c6924cb6f025"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"125a748e9305bf2ff330e307b52ac99f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"5867a832ae87b5e70284d8987d79b4b3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"91fa72e09a0f06350440c87d81ae500c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"1783f60bcbf1764090536df245866c22"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","hash":"7e8748e5cc7add4cc9cb8aa16f88bf0b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","hash":"6b0c8e15ba72bff3e671eaac675f60fc"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"fc62522a963025f3b7fc2c6502083fe7"}]} \ No newline at end of file +{"version":2,"files":[{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart","hash":"e1c2dd402ece615704eaf45f3e2729a6"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"45c460142089a4ea87608b06ff2628b1"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"25dad41ccf2f92d2c497c7800a1ce45c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart","hash":"b4d03024c1c40f7d5353ebced85205a2"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart","hash":"ac39db85fddd532a36bc0ad2ff7a65da"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"4f2fe3f8c87899e023e72b5a170061aa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart","hash":"ceed420aa54a45d5cf8b74721098e1cd"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart","hash":"249fa76b027a367d4d3175dc97a91fca"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"09e4f926e05cee727e7f9a32cc98b812"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart","hash":"712abca07c4b95d9ff1d4d49e5a0ba93"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"9dedf57678d81d80efcd3a706a4d7a30"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"5a8a246b080e3003cf8f728d2b10b6f9"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"24b4e737f1bef65c324059937e559819"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_id.dart","hash":"498d474db7361219be2b1a84a382d4df"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_el.dart","hash":"2d9ed54e233b37087ec75a58e2cb5e1b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_en.dart","hash":"aace294470f29d70e75ae5e6e2cbd6c3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart","hash":"b18042dbed118bcf827f7e76bad1f86f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart","hash":"bcc9f15b7bb9e2853491d54d0452def2"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations.dart","hash":"5d9ebe82abf371f5e62696bae72eefaa"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart","hash":"5c0867a5c13989b8603cc22375d5464c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"68c77aa51f2ba7ce1cc24a7ed22a648b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"ee2dfa2a8e23ad82d27e32d499083189"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"ebfcd08d4c861cc0c5d2d5371e430e56"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart","hash":"5329b0b8d1335418b8e7c28eb6b97acb"},{"path":"/Users/vishnu/tools/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"5ff2d90544438cc086555a06fe71334b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/l10n.yaml","hash":"7f1fe83b9f37bd55ea049a6a2e5d1abe"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart","hash":"bada8a24323a9032a1d48f99d364f7f3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"f5e6b9da5f1fce53cb7a13ed0ac928d4"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_da.dart","hash":"97848131d81cb0c62ef4500104178d3a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"72a20184b7c14aa3bebc1829168ec249"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"b8b1ab1d8d851e1efdaac119ca9aed51"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart","hash":"e281bb4412dfed8559e8bc670ae9b7f7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_es.dart","hash":"9ad9b82614ff0ab5763c9cae9644b8d6"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"a8421a85bad5a6adbff705117f683290"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart","hash":"3837a1da249e7377d67ca12fddf61708"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"05da370179c31437bac685fae12ba5e3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"faab6eda68e6fc4782603410686f468a"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"b344d11d93750b02904d1ee4b1e40d9e"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart","hash":"3fa1f5ae77f6ee3ae37a2af7ba5c4e17"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"55ad5a67f7709ca095b64d46c68feaf7"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart","hash":"f66aa0ae4b9b07a3611fda4e3312be41"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"125a748e9305bf2ff330e307b52ac99f"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"5867a832ae87b5e70284d8987d79b4b3"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"91fa72e09a0f06350440c87d81ae500c"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"1783f60bcbf1764090536df245866c22"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart","hash":"7e6e759e326dd57ef8fcf50e7b09709b"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart","hash":"39dae1cf59fe1386ca5813e7f9d99a89"},{"path":"/Users/vishnu/work/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"fc62522a963025f3b7fc2c6502083fe7"}]} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index 56f153543e..5daf0f0c71 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -1220,18 +1220,6 @@ abstract class StringsLocalizations { /// **'No recovery key'** String get noRecoveryKey; - /// Status message while waiting for WiFi connection - /// - /// In en, this message translates to: - /// **'Waiting for WiFi...'** - String get waitingForWifi; - - /// Error message when attempting to delete the default account - /// - /// In en, this message translates to: - /// **'You cannot delete your default account'** - String get youCannotDeleteYourDefaultAccount; - /// Confirmation message when account has been deleted /// /// In en, this message translates to: diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index adfe676f5d..8ebac5e778 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -603,13 +603,6 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 2586324842..14cf505ae9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -603,13 +603,6 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index dbce15788d..09e0bc5800 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -603,13 +603,6 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index d7f0abc3df..ed28c5db33 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -603,13 +603,6 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 87ced4197d..605e90acd3 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -603,13 +603,6 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 01f51c2561..2a1f3e5b95 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -603,13 +603,6 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 6c1e0aaecf..7c20ebc7bb 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -603,13 +603,6 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 4109cf4fba..4d248e0f09 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -603,13 +603,6 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 16546527ba..75aceba5c0 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -603,13 +603,6 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 56a398d9d3..007a5eafdd 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -603,13 +603,6 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index b144ae51cf..62edc10f95 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -603,13 +603,6 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 036dd81fec..49e7f4eed4 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -603,13 +603,6 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 6a95f9f7d5..fb44290723 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -603,13 +603,6 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index c72f89835c..6e5029db99 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -603,13 +603,6 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index b848a94a68..0fec72c8f5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -603,13 +603,6 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index 753036325e..51db2ed222 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -603,13 +603,6 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 95c8373d02..c61b186331 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -603,13 +603,6 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 63ddbf5858..04a4352ad8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -603,13 +603,6 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 81aedc19a1..0f55425c09 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -603,13 +603,6 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index 0f95b247d8..e4b060a3f1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -603,13 +603,6 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 742890b81a..6cb43475bc 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -603,13 +603,6 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 51775fe459..61b0924d06 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -602,13 +602,6 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get noRecoveryKey => 'No recovery key'; - @override - String get waitingForWifi => 'Waiting for WiFi...'; - - @override - String get youCannotDeleteYourDefaultAccount => - 'You cannot delete your default account'; - @override String get yourAccountHasBeenDeleted => 'Your account has been deleted'; From b84b73fda2f03396cea58280b302bf6ad004858c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:44:06 +0530 Subject: [PATCH 038/164] Remove changelog --- mobile/packages/strings/CHANGELOG.md | 41 ---------------------------- 1 file changed, 41 deletions(-) delete mode 100644 mobile/packages/strings/CHANGELOG.md diff --git a/mobile/packages/strings/CHANGELOG.md b/mobile/packages/strings/CHANGELOG.md deleted file mode 100644 index b30256ddba..0000000000 --- a/mobile/packages/strings/CHANGELOG.md +++ /dev/null @@ -1,41 +0,0 @@ -# Changelog - -All notable changes to the `ente_strings` package will be documented in this file. - -## [1.0.0] - 2025-07-19 - -### Added -- Initial release of the `ente_strings` package -- Centralized localization for common strings across Ente apps -- Support for 22 languages with `networkHostLookUpErr` string: - - Arabic (ar) - - Bulgarian (bg) - - Czech (cs) - - Danish (da) - - Greek (el) - - English (en) - - French (fr) - - Indonesian (id) - - Japanese (ja) - - Korean (ko) - - Lithuanian (lt) - - Dutch (nl) - - Polish (pl) - - Portuguese (pt) - - Russian (ru) - - Slovak (sk) - - Serbian (sr) - - Swedish (sv) - - Turkish (tr) - - Vietnamese (vi) - - Chinese Simplified (zh) - - Chinese Traditional (zh_TW) -- Convenient `EnteStringsExtension` for easy access via `context.strings` -- Complete documentation and example app -- Unit tests for localization functionality - -### Features -- Flutter localization generation support -- Integration with `flutter_localizations` -- Type-safe string access -- Support for both extension and traditional localization access patterns From de04f18cb2c9f46aa6d298f3f67bda6146213ed7 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:02:39 +0530 Subject: [PATCH 039/164] Remove noise --- mobile/packages/strings/example/main.dart | 90 ------- mobile/packages/strings/example/pubspec.lock | 225 ------------------ mobile/packages/strings/example/pubspec.yaml | 22 -- .../ui/lib/theme/color_migration_demo.dart | 206 ---------------- .../ui/lib/theme/color_system_test.dart | 161 ------------- mobile/packages/ui/lib/theme/colors.dart | 2 - .../ui/lib/theme/example_app_colors.dart | 152 ------------ 7 files changed, 858 deletions(-) delete mode 100644 mobile/packages/strings/example/main.dart delete mode 100644 mobile/packages/strings/example/pubspec.lock delete mode 100644 mobile/packages/strings/example/pubspec.yaml delete mode 100644 mobile/packages/ui/lib/theme/color_migration_demo.dart delete mode 100644 mobile/packages/ui/lib/theme/color_system_test.dart delete mode 100644 mobile/packages/ui/lib/theme/example_app_colors.dart diff --git a/mobile/packages/strings/example/main.dart b/mobile/packages/strings/example/main.dart deleted file mode 100644 index e9533a11fa..0000000000 --- a/mobile/packages/strings/example/main.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:ente_strings/ente_strings.dart'; - -/// Example widget demonstrating how to use the ente_strings package -class ExampleStringUsage extends StatelessWidget { - const ExampleStringUsage({super.key}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Ente Strings Example'), - ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Text( - 'Network Error Message:', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - // Example 1: Using the extension method - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.red[50], - border: Border.all(color: Colors.red[200]!), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - context.strings.networkHostLookUpErr, - style: const TextStyle(color: Colors.red), - ), - ), - const SizedBox(height: 16), - const Text( - 'Alternative usage:', - style: TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 8), - // Example 2: Using the traditional approach - Container( - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.orange[50], - border: Border.all(color: Colors.orange[200]!), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - StringsLocalizations.of(context).networkHostLookUpErr, - style: const TextStyle(color: Colors.orange), - ), - ), - ], - ), - ), - ); - } -} - -/// Example main app demonstrating setup -class ExampleApp extends StatelessWidget { - const ExampleApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Ente Strings Example', - // Configure localization delegates - localizationsDelegates: [ - ...StringsLocalizations.localizationsDelegates, - // Add your other app-specific delegates here - ], - supportedLocales: StringsLocalizations.supportedLocales, - home: const ExampleStringUsage(), - ); - } -} - -void main() { - runApp(const ExampleApp()); -} diff --git a/mobile/packages/strings/example/pubspec.lock b/mobile/packages/strings/example/pubspec.lock deleted file mode 100644 index bec753b599..0000000000 --- a/mobile/packages/strings/example/pubspec.lock +++ /dev/null @@ -1,225 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - characters: - dependency: transitive - description: - name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - collection: - dependency: transitive - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - ente_strings: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "1.0.0" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.dev" - source: hosted - version: "5.0.0" - flutter_localizations: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - intl: - dependency: transitive - description: - name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" - source: hosted - version: "0.20.2" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" - url: "https://pub.dev" - source: hosted - version: "10.0.9" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.dev" - source: hosted - version: "3.0.9" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.dev" - source: hosted - version: "5.1.1" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" - source: hosted - version: "0.12.17" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: transitive - description: - name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" - source: hosted - version: "1.16.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" - source: hosted - version: "1.10.1" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.dev" - source: hosted - version: "0.7.4" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 - url: "https://pub.dev" - source: hosted - version: "15.0.0" -sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/strings/example/pubspec.yaml b/mobile/packages/strings/example/pubspec.yaml deleted file mode 100644 index 4f369c23e0..0000000000 --- a/mobile/packages/strings/example/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: ente_strings_example -description: Example app demonstrating the ente_strings package -version: 1.0.0 -publish_to: none - -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=1.17.0" - -dependencies: - flutter: - sdk: flutter - ente_strings: - path: ../ - -dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^5.0.0 - -flutter: - uses-material-design: true diff --git a/mobile/packages/ui/lib/theme/color_migration_demo.dart b/mobile/packages/ui/lib/theme/color_migration_demo.dart deleted file mode 100644 index 0f667ea910..0000000000 --- a/mobile/packages/ui/lib/theme/color_migration_demo.dart +++ /dev/null @@ -1,206 +0,0 @@ -// Demo showing the color system migration is complete -// This file demonstrates that the new color system works correctly - -import 'package:flutter/material.dart'; -import '../theme/colors.dart'; -import '../theme/ente_theme.dart'; -import '../theme/ente_theme_data.dart'; - -/// Demo widget showing the new color system in action -class ColorMigrationDemo extends StatelessWidget { - const ColorMigrationDemo({super.key}); - - @override - Widget build(BuildContext context) { - // NEW: Preferred way to access colors - final colorScheme = Theme.of(context).extension() ?? - getEnteColorScheme(context); - - return Scaffold( - backgroundColor: colorScheme.backgroundBase, - appBar: AppBar( - backgroundColor: colorScheme.backgroundElevated, - title: Text( - 'Color Migration Demo', - style: TextStyle(color: colorScheme.textBase), - ), - ), - body: SingleChildScrollView( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildSection( - colorScheme, - 'Background Colors', - color: colorScheme.backgroundElevated, - border: colorScheme.strokeFaint, - ), - const SizedBox(height: 16), - _buildSection( - colorScheme, - 'Primary Colors', - color: colorScheme.primary500, - textColor: colorScheme.backgroundBase, - ), - const SizedBox(height: 16), - _buildGradientSection(colorScheme), - const SizedBox(height: 16), - _buildSection( - colorScheme, - 'Warning Colors', - color: colorScheme.warning500.withOpacity(0.1), - border: colorScheme.warning500, - textColor: colorScheme.warning700, - ), - const SizedBox(height: 16), - _buildSection( - colorScheme, - 'Fill Colors', - color: colorScheme.fillFaint, - textColor: colorScheme.textMuted, - ), - const SizedBox(height: 16), - _buildMigrationInfo(colorScheme), - ], - ), - ), - ); - } - - Widget _buildSection( - EnteColorScheme colorScheme, - String title, { - required Color color, - Color? border, - Color? textColor, - }) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: color, - border: border != null ? Border.all(color: border) : null, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - '$title ✅', - style: TextStyle( - color: textColor ?? colorScheme.textBase, - fontWeight: FontWeight.w500, - ), - ), - ); - } - - Widget _buildGradientSection(EnteColorScheme colorScheme) { - return Container( - height: 60, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: colorScheme.gradientButtonBgColors, - ), - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Text( - 'Gradient Colors ✅', - style: TextStyle( - color: colorScheme.backgroundBase, - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - ), - ); - } - - Widget _buildMigrationInfo(EnteColorScheme colorScheme) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: colorScheme.primary500.withOpacity(0.1), - border: Border.all(color: colorScheme.primary500), - borderRadius: BorderRadius.circular(8), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Migration Complete! 🎉', - style: TextStyle( - color: colorScheme.primary700, - fontWeight: FontWeight.bold, - fontSize: 18, - ), - ), - const SizedBox(height: 8), - Text( - '• All components migrated to new color system\n' - '• Theme-aware colors with fallback support\n' - '• Custom branding support ready\n' - '• Performance optimized with const colors\n' - '• Type-safe with compile-time validation', - style: TextStyle( - color: colorScheme.textBase, - height: 1.5, - ), - ), - ], - ), - ); - } -} - -/// Example of creating custom app themes -class CustomAppThemeExample { - // Purple brand theme - static final purpleScheme = ColorSchemeBuilder.fromPrimaryColor( - const Color(0xFF6C5CE7), - ); - - // Blue brand theme - static final blueScheme = ColorSchemeBuilder.fromPrimaryColor( - const Color(0xFF2196F3), - ); - - // Green brand theme - static final greenScheme = ColorSchemeBuilder.fromPrimaryColor( - const Color(0xFF4CAF50), - ); - - // Create theme data for each brand - static ThemeData purpleLightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: purpleScheme.light, - ); - - static ThemeData purpleDarkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: purpleScheme.dark, - ); - - static ThemeData blueLightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: blueScheme.light, - ); - - static ThemeData blueDarkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: blueScheme.dark, - ); -} - -/// Example app showing how to use the new color system -class ColorSystemExampleApp extends StatelessWidget { - const ColorSystemExampleApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Color System Demo', - theme: CustomAppThemeExample.blueLightTheme, - darkTheme: CustomAppThemeExample.blueDarkTheme, - home: const ColorMigrationDemo(), - ); - } -} diff --git a/mobile/packages/ui/lib/theme/color_system_test.dart b/mobile/packages/ui/lib/theme/color_system_test.dart deleted file mode 100644 index 00f8018a51..0000000000 --- a/mobile/packages/ui/lib/theme/color_system_test.dart +++ /dev/null @@ -1,161 +0,0 @@ -// Test to validate the color system migration -// This file is for verification purposes only - -import 'package:flutter/material.dart'; -import '../theme/colors.dart'; -import '../theme/ente_theme.dart'; -import '../theme/ente_theme_data.dart'; - -/// Test widget to verify the new color system works correctly -class ColorSystemTestWidget extends StatelessWidget { - const ColorSystemTestWidget({super.key}); - - @override - Widget build(BuildContext context) { - // Test that we can access the color scheme from theme - final colorScheme = Theme.of(context).extension(); - - // Test that fallback to old system works - final fallbackColorScheme = colorScheme ?? getEnteColorScheme(context); - - return Scaffold( - backgroundColor: fallbackColorScheme.backgroundBase, - appBar: AppBar( - backgroundColor: fallbackColorScheme.backgroundElevated, - title: Text( - 'Color System Test', - style: TextStyle(color: fallbackColorScheme.textBase), - ), - ), - body: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Test basic colors - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: fallbackColorScheme.backgroundElevated, - border: Border.all(color: fallbackColorScheme.strokeFaint), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - 'Background Colors Working', - style: TextStyle(color: fallbackColorScheme.textBase), - ), - ), - const SizedBox(height: 16), - - // Test primary colors - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: fallbackColorScheme.primary500, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - 'Primary Colors Working', - style: TextStyle(color: fallbackColorScheme.backgroundBase), - ), - ), - const SizedBox(height: 16), - - // Test gradient colors - Container( - height: 50, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: fallbackColorScheme.gradientButtonBgColors, - ), - borderRadius: BorderRadius.circular(8), - ), - child: Center( - child: Text( - 'Gradient Colors Working', - style: TextStyle( - color: fallbackColorScheme.backgroundBase, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - const SizedBox(height: 16), - - // Test warning colors - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: fallbackColorScheme.warning500.withOpacity(0.1), - border: Border.all(color: fallbackColorScheme.warning500), - borderRadius: BorderRadius.circular(8), - ), - child: Text( - 'Warning Colors Working', - style: TextStyle(color: fallbackColorScheme.warning700), - ), - ), - const SizedBox(height: 16), - - // Test fill colors - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: fallbackColorScheme.fillFaint, - borderRadius: BorderRadius.circular(8), - ), - child: Text( - 'Fill Colors Working', - style: TextStyle(color: fallbackColorScheme.textMuted), - ), - ), - ], - ), - ), - ); - } -} - -/// Example showing how apps can create custom themes -class CustomThemeExample { - // Create a custom purple theme - static final purpleSchemes = ColorSchemeBuilder.fromPrimaryColor( - const Color(0xFF6C5CE7), // Purple brand color - ); - - static final lightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: purpleSchemes.light, - ); - - static final darkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: purpleSchemes.dark, - ); -} - -/// Example showing migration from old to new system -class MigrationExample extends StatelessWidget { - const MigrationExample({super.key}); - - @override - Widget build(BuildContext context) { - // OLD WAY (still works for backward compatibility) - // final colorScheme = getEnteColorScheme(context); - - // NEW WAY (preferred) - // final colorScheme = Theme.of(context).extension()!; - - // SAFE WAY (with fallback) - final safeColorScheme = Theme.of(context).extension() ?? - getEnteColorScheme(context); - - return Container( - color: safeColorScheme.backgroundBase, - child: Text( - 'Migration Example', - style: TextStyle(color: safeColorScheme.textBase), - ), - ); - } -} diff --git a/mobile/packages/ui/lib/theme/colors.dart b/mobile/packages/ui/lib/theme/colors.dart index aa0f6274c5..17a641037f 100644 --- a/mobile/packages/ui/lib/theme/colors.dart +++ b/mobile/packages/ui/lib/theme/colors.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; -/// A comprehensive color scheme for consistent theming across apps. -/// /// This color scheme provides all the colors needed for a modern Flutter app, /// including background, text, fill, stroke, and accent colors for both light /// and dark themes. diff --git a/mobile/packages/ui/lib/theme/example_app_colors.dart b/mobile/packages/ui/lib/theme/example_app_colors.dart deleted file mode 100644 index 22ee8b99f8..0000000000 --- a/mobile/packages/ui/lib/theme/example_app_colors.dart +++ /dev/null @@ -1,152 +0,0 @@ -// Example: How to use the reusable EnteColorScheme in your app -// filepath: example_app_colors.dart - -import 'package:flutter/material.dart'; -import 'colors.dart'; // Import the reusable color scheme -import 'ente_theme_data.dart'; // Import the theme data helper -import 'ente_theme.dart'; // Import for getEnteColorScheme - -/// Example 1: Using the default color scheme -class DefaultThemeExample { - static final lightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: lightScheme, - ); - - static final darkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: darkScheme, - ); -} - -/// Example 2: Creating a custom theme with brand colors -class CustomBrandThemeExample { - // Define your app's brand colors - static const Color brandPrimaryColor = Color(0xFF6C5CE7); // Purple - - static final schemes = ColorSchemeBuilder.fromPrimaryColor(brandPrimaryColor); - - static final lightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: schemes.light, - ); - - static final darkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: schemes.dark, - ); -} - -/// Example 3: Creating a theme with fully custom primary colors -class FullyCustomThemeExample { - static final schemes = ColorSchemeBuilder.fromCustomColors( - primary700: const Color(0xFF1565C0), // Dark blue - primary500: const Color(0xFF2196F3), // Material blue - primary400: const Color(0xFF42A5F5), // Light blue - primary300: const Color(0xFF90CAF9), // Very light blue - iconButtonColor: const Color(0xFF1976D2), // Custom icon color - gradientButtonBgColors: const [ - Color(0xFF1565C0), - Color(0xFF2196F3), - Color(0xFF42A5F5), - ], - ); - - static final lightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: schemes.light, - ); - - static final darkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: schemes.dark, - ); -} - -/// Example 4: Using factory constructors for fine-grained control -class FactoryConstructorExample { - static final lightScheme = EnteColorScheme.light( - primary700: const Color(0xFFE91E63), // Pink 700 - primary500: const Color(0xFFF06292), // Pink 300 - primary400: const Color(0xFFF8BBD9), // Pink 200 - primary300: const Color(0xFFFCE4EC), // Pink 50 - warning500: const Color(0xFFFF5722), // Custom warning color - ); - - static final darkScheme = EnteColorScheme.dark( - primary700: const Color(0xFFE91E63), - primary500: const Color(0xFFF06292), - primary400: const Color(0xFFF8BBD9), - primary300: const Color(0xFFFCE4EC), - warning500: const Color(0xFFFF5722), - ); - - static final lightTheme = createAppThemeData( - brightness: Brightness.light, - colorScheme: lightScheme, - ); - - static final darkTheme = createAppThemeData( - brightness: Brightness.dark, - colorScheme: darkScheme, - ); -} - -/// Helper function to get the current color scheme from context -EnteColorScheme getColorScheme(BuildContext context) { - return getEnteColorScheme(context); -} - -/// Example widget showing how to use the color scheme in your UI -class ExampleWidget extends StatelessWidget { - const ExampleWidget({super.key}); - - @override - Widget build(BuildContext context) { - final colorScheme = getColorScheme(context); - - return Container( - color: colorScheme.backgroundBase, - child: Column( - children: [ - Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: colorScheme.backgroundElevated, - border: Border.all(color: colorScheme.strokeFaint), - ), - child: Text( - 'Example Text', - style: TextStyle(color: colorScheme.textBase), - ), - ), - ElevatedButton( - style: ElevatedButton.styleFrom( - backgroundColor: colorScheme.primary500, - foregroundColor: colorScheme.backgroundBase, - ), - onPressed: () {}, - child: const Text('Primary Button'), - ), - Container( - height: 50, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: colorScheme.gradientButtonBgColors, - ), - ), - child: Center( - child: Text( - 'Gradient Button', - style: TextStyle( - color: colorScheme.backgroundBase, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ), - ); - } -} From a510320d0efa282efbb49cd47b6a6a7918150fd5 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:12:57 +0530 Subject: [PATCH 040/164] Lint accounts --- .../packages/accounts/analysis_options.yaml | 71 +++++++++++++++++++ .../packages/accounts/lib/ente_accounts.dart | 18 +++-- .../accounts/lib/models/user_details.dart | 4 +- .../lib/pages/change_email_dialog.dart | 2 +- .../lib/pages/delete_account_page.dart | 6 +- .../accounts/lib/pages/login_page.dart | 2 +- .../pages/login_pwd_verification_page.dart | 4 +- .../lib/pages/ott_verification_page.dart | 2 +- .../accounts/lib/pages/passkey_page.dart | 2 +- .../lib/pages/password_entry_page.dart | 6 +- .../lib/pages/password_reentry_page.dart | 5 +- .../accounts/lib/pages/recovery_key_page.dart | 2 +- .../accounts/lib/pages/recovery_page.dart | 2 +- .../pages/request_pwd_verification_page.dart | 4 +- .../accounts/lib/pages/sessions_page.dart | 4 +- .../pages/two_factor_authentication_page.dart | 4 +- .../lib/pages/two_factor_recovery_page.dart | 2 +- .../accounts/lib/services/user_service.dart | 12 ++-- mobile/packages/accounts/pubspec.lock | 9 +-- mobile/packages/accounts/pubspec.yaml | 60 ++++++++-------- 20 files changed, 144 insertions(+), 77 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index f9b303465f..1bd78bc1b0 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index e7865b2f1b..858677e3f9 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,21 +1,16 @@ /// A Flutter package containing account-related functionality for Ente apps library ente_accounts; -// Models -export 'models/user_details.dart'; -export 'models/sessions.dart'; -export 'models/two_factor.dart'; -export 'models/srp.dart'; +export 'models/bonus.dart'; export 'models/delete_account.dart'; +export 'models/sessions.dart'; export 'models/set_keys_request.dart'; export 'models/set_recovery_key_request.dart'; -export 'models/bonus.dart'; +export 'models/srp.dart'; export 'models/subscription.dart'; +export 'models/two_factor.dart'; +export 'models/user_details.dart'; -// Services -export 'services/user_service.dart'; - -// Pages export 'pages/change_email_dialog.dart'; export 'pages/delete_account_page.dart'; export 'pages/email_entry_page.dart'; @@ -31,3 +26,6 @@ export 'pages/request_pwd_verification_page.dart'; export 'pages/sessions_page.dart'; export 'pages/two_factor_authentication_page.dart'; export 'pages/two_factor_recovery_page.dart'; + +export 'services/passkey_service.dart'; +export 'services/user_service.dart'; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index bae0311a7f..7fe5f8e60f 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:math'; -import 'bonus.dart'; -import 'subscription.dart'; +import 'package:ente_accounts/models/bonus.dart'; +import 'package:ente_accounts/models/subscription.dart'; class UserDetails { final String email; diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index 0cc49f25bc..860b627918 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class ChangeEmailDialog extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index b06ac5e4d5..66963914d7 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -4,13 +4,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; -import 'package:ente_ui/components/buttons/gradient_button.dart'; -import 'package:ente_ui/components/dialogs.dart'; class DeleteAccountPage extends StatelessWidget { final BaseConfiguration configuration; diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index 17737cc0e0..c51b74e8a4 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -2,10 +2,10 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import "package:ente_strings/ente_strings.dart"; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; -import "package:ente_strings/ente_strings.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import "package:styled_text/styled_text.dart"; diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart index 6c0144f3b0..e23064d389 100644 --- a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -1,13 +1,13 @@ import "package:dio/dio.dart"; import "package:ente_accounts/ente_accounts.dart"; import "package:ente_configuration/base_configuration.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; import "package:ente_utils/email_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart index 442401fbd7..cc49d1f88d 100644 --- a/mobile/packages/accounts/lib/pages/ott_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; import 'package:styled_text/styled_text.dart'; diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart index 584be1bceb..f7470939da 100644 --- a/mobile/packages/accounts/lib/pages/passkey_page.dart +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -4,12 +4,12 @@ import 'package:app_links/app_links.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index bf171e3cc3..36152d883b 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -1,5 +1,6 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/pages/base_home_page.dart'; @@ -8,7 +9,6 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; @@ -448,7 +448,9 @@ class _PasswordEntryPageState extends State { bool usingVolatilePassword = false, }) async { final dialog = createProgressDialog( - context, context.strings.generatingEncryptionKeysTitle); + context, + context.strings.generatingEncryptionKeysTitle, + ); await dialog.show(); try { if (usingVolatilePassword) { diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 0de41c1992..7e081c9a5c 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -4,17 +4,16 @@ import 'dart:typed_data'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; import 'package:flutter/material.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:logging/logging.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; - class PasswordReentryPage extends StatefulWidget { final BaseHomePage homePage; final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 20f49b8f52..3cf2249771 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -5,12 +5,12 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:dotted_border/dotted_border.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index 0901452a11..cc889bc34c 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart index 0fa74243ad..d3d23a1096 100644 --- a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -2,11 +2,11 @@ import "dart:convert"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart index 29774497ee..dbbcb9bf98 100644 --- a/mobile/packages/accounts/lib/pages/sessions_page.dart +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -1,13 +1,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/loading_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_utils/date_time_util.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:ente_utils/date_time_util.dart'; class SessionsPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart index 58a7312e5a..f1fa4f327e 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinput/pinput.dart'; -import 'package:ente_ui/lifecycle_event_handler.dart'; class TwoFactorAuthenticationPage extends StatefulWidget { final String sessionID; diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart index 12c6da59aa..878a8a2ba8 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class TwoFactorRecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index 8af3d55973..f127fe50be 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -20,19 +20,19 @@ import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_accounts/pages/recovery_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; import 'package:ente_network/network.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/progress_dialog.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 8fee60bb47..1dd9150ce5 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -409,10 +409,11 @@ packages: flutter_local_authentication: dependency: transitive description: - name: flutter_local_authentication - sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd - url: "https://pub.dev" - source: hosted + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index eca03a2a01..1d4064ac75 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -7,54 +7,50 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter - - # Ente packages + app_links: ^6.3.3 + bip39: ^1.0.6 + collection: ^1.18.0 + dio: ^5.4.0 + dotted_border: ^3.1.0 + email_validator: ^3.0.0 + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../events + ente_lock_screen: + path: ../lock_screen + ente_network: + path: ../network ente_strings: path: ../strings ente_ui: path: ../ui ente_utils: path: ../utils - ente_base: - path: ../base - ente_configuration: - path: ../configuration - ente_network: - path: ../network - ente_events: - path: ../events - ente_crypto_dart: - git: - url: https://github.com/ente-io/ente_crypto_dart.git - ente_lock_screen: - path: ../lock_screen - - # Third-party dependencies - bip39: ^1.0.6 - dio: ^5.4.0 - logging: ^1.2.0 - pointycastle: ^3.7.3 - shared_preferences: ^2.2.2 file_saver: ^0.3.0 - share_plus: ^11.0.0 - uuid: ^4.2.1 - collection: ^1.18.0 - dotted_border: ^3.1.0 + flutter: + sdk: flutter + logging: ^1.2.0 password_strength: ^0.2.0 + pinput: ^5.0.1 + pointycastle: ^3.7.3 + share_plus: ^11.0.0 + shared_preferences: ^2.2.2 step_progress_indicator: ^1.0.2 styled_text: ^8.1.0 - email_validator: ^3.0.0 - pinput: ^5.0.1 - app_links: ^6.3.3 url_launcher: ^6.3.1 url_launcher_ios: ^6.3.1 + uuid: ^4.2.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 860b2895f6813942cf8dc17c81950ac3809332f1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:17 +0530 Subject: [PATCH 041/164] Fix minor lint --- mobile/packages/accounts/lib/ente_accounts.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index 858677e3f9..9fcaf2bcf6 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,6 +1,3 @@ -/// A Flutter package containing account-related functionality for Ente apps -library ente_accounts; - export 'models/bonus.dart'; export 'models/delete_account.dart'; export 'models/sessions.dart'; From 24f5a5813a96ce6cd9daa93e1692311e18f3b70b Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:28 +0530 Subject: [PATCH 042/164] Lint base --- mobile/packages/base/analysis_options.yaml | 68 ++++++++++++++++++- mobile/packages/base/lib/ente_base.dart | 2 - .../base/lib/models/key_gen_result.dart | 4 +- mobile/packages/base/pubspec.yaml | 2 +- 4 files changed, 68 insertions(+), 8 deletions(-) diff --git a/mobile/packages/base/analysis_options.yaml b/mobile/packages/base/analysis_options.yaml index 609eb5d8aa..1bd78bc1b0 100644 --- a/mobile/packages/base/analysis_options.yaml +++ b/mobile/packages/base/analysis_options.yaml @@ -1,10 +1,72 @@ -include: package:flutter_lints/flutter.yaml +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks +include: package:flutter_lints/flutter.yaml linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/base/lib/ente_base.dart b/mobile/packages/base/lib/ente_base.dart index ed2182bd4d..6bb3610d5d 100644 --- a/mobile/packages/base/lib/ente_base.dart +++ b/mobile/packages/base/lib/ente_base.dart @@ -1,5 +1,3 @@ -library ente_base; - export 'models/database.dart'; export 'models/key_attributes.dart'; export 'models/key_gen_result.dart'; diff --git a/mobile/packages/base/lib/models/key_gen_result.dart b/mobile/packages/base/lib/models/key_gen_result.dart index 27dd062229..758b8dd690 100644 --- a/mobile/packages/base/lib/models/key_gen_result.dart +++ b/mobile/packages/base/lib/models/key_gen_result.dart @@ -1,7 +1,7 @@ import "dart:typed_data"; -import 'key_attributes.dart'; -import 'private_key_attributes.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/private_key_attributes.dart'; class KeyGenResult { final KeyAttributes keyAttributes; diff --git a/mobile/packages/base/pubspec.yaml b/mobile/packages/base/pubspec.yaml index 04aeb70512..3148d560e1 100644 --- a/mobile/packages/base/pubspec.yaml +++ b/mobile/packages/base/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 957f0bc04138639de33abfe12795c83c97ab13fb Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:15:23 +0530 Subject: [PATCH 043/164] Lint configuration --- .../configuration/analysis_options.yml | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 mobile/packages/configuration/analysis_options.yml diff --git a/mobile/packages/configuration/analysis_options.yml b/mobile/packages/configuration/analysis_options.yml new file mode 100644 index 0000000000..1bd78bc1b0 --- /dev/null +++ b/mobile/packages/configuration/analysis_options.yml @@ -0,0 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + +include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides From c9a79183971508d19775f86c446bf8b25b6ca361 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:16:24 +0530 Subject: [PATCH 044/164] Lint events --- mobile/packages/events/analysis_options.yaml | 68 ++++++++++++++++++- mobile/packages/events/lib/ente_events.dart | 9 +-- .../lib/models/collections_updated_event.dart | 2 +- .../lib/models/endpoint_updated_event.dart | 2 +- .../events/lib/models/signed_in_event.dart | 2 +- .../events/lib/models/signed_out_event.dart | 2 +- .../lib/models/trigger_logout_event.dart | 2 +- .../models/user_details_changed_event.dart | 2 +- mobile/packages/events/pubspec.yaml | 4 +- 9 files changed, 75 insertions(+), 18 deletions(-) diff --git a/mobile/packages/events/analysis_options.yaml b/mobile/packages/events/analysis_options.yaml index 609eb5d8aa..1bd78bc1b0 100644 --- a/mobile/packages/events/analysis_options.yaml +++ b/mobile/packages/events/analysis_options.yaml @@ -1,10 +1,72 @@ -include: package:flutter_lints/flutter.yaml +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks +include: package:flutter_lints/flutter.yaml linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/events/lib/ente_events.dart b/mobile/packages/events/lib/ente_events.dart index 023d166e49..a4a94dd52d 100644 --- a/mobile/packages/events/lib/ente_events.dart +++ b/mobile/packages/events/lib/ente_events.dart @@ -1,13 +1,8 @@ -library ente_events; - -// Export the event bus export 'event_bus.dart'; - -// Export all event models +export 'models/collections_updated_event.dart'; +export 'models/endpoint_updated_event.dart'; export 'models/event.dart'; export 'models/signed_in_event.dart'; export 'models/signed_out_event.dart'; export 'models/trigger_logout_event.dart'; export 'models/user_details_changed_event.dart'; -export 'models/endpoint_updated_event.dart'; -export 'models/collections_updated_event.dart'; diff --git a/mobile/packages/events/lib/models/collections_updated_event.dart b/mobile/packages/events/lib/models/collections_updated_event.dart index 5fb0736a26..d35e3a15cf 100644 --- a/mobile/packages/events/lib/models/collections_updated_event.dart +++ b/mobile/packages/events/lib/models/collections_updated_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class CollectionsUpdatedEvent extends Event {} diff --git a/mobile/packages/events/lib/models/endpoint_updated_event.dart b/mobile/packages/events/lib/models/endpoint_updated_event.dart index 33c88d420d..ccdf0daa4f 100644 --- a/mobile/packages/events/lib/models/endpoint_updated_event.dart +++ b/mobile/packages/events/lib/models/endpoint_updated_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class EndpointUpdatedEvent extends Event {} diff --git a/mobile/packages/events/lib/models/signed_in_event.dart b/mobile/packages/events/lib/models/signed_in_event.dart index 15ae9cedc9..db30d50c84 100644 --- a/mobile/packages/events/lib/models/signed_in_event.dart +++ b/mobile/packages/events/lib/models/signed_in_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class SignedInEvent extends Event {} diff --git a/mobile/packages/events/lib/models/signed_out_event.dart b/mobile/packages/events/lib/models/signed_out_event.dart index 84e7dc7822..8fe1055197 100644 --- a/mobile/packages/events/lib/models/signed_out_event.dart +++ b/mobile/packages/events/lib/models/signed_out_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class SignedOutEvent extends Event {} diff --git a/mobile/packages/events/lib/models/trigger_logout_event.dart b/mobile/packages/events/lib/models/trigger_logout_event.dart index 48b068ffce..a6c9b724f5 100644 --- a/mobile/packages/events/lib/models/trigger_logout_event.dart +++ b/mobile/packages/events/lib/models/trigger_logout_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class TriggerLogoutEvent extends Event {} diff --git a/mobile/packages/events/lib/models/user_details_changed_event.dart b/mobile/packages/events/lib/models/user_details_changed_event.dart index 9cd352a2ac..d96a038a69 100644 --- a/mobile/packages/events/lib/models/user_details_changed_event.dart +++ b/mobile/packages/events/lib/models/user_details_changed_event.dart @@ -1,3 +1,3 @@ -import 'event.dart'; +import 'package:ente_events/models/event.dart'; class UserDetailsChangedEvent extends Event {} diff --git a/mobile/packages/events/pubspec.yaml b/mobile/packages/events/pubspec.yaml index dc21a6ca95..a68822fcfe 100644 --- a/mobile/packages/events/pubspec.yaml +++ b/mobile/packages/events/pubspec.yaml @@ -7,13 +7,13 @@ environment: flutter: ">=1.17.0" dependencies: + event_bus: ^2.0.1 flutter: sdk: flutter - event_bus: ^2.0.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 0455481f3d5cbf6f7ccbaeb0e6c12bd069baedf8 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:21:53 +0530 Subject: [PATCH 045/164] Lint lock_screen --- .../lock_screen/analysis_options.yaml | 72 +++++++++++++++++++ .../packages/lock_screen/lib/auth_util.dart | 4 +- .../lib/local_authentication_service.dart | 10 +-- .../lock_screen/lib/lock_screen_settings.dart | 4 +- .../packages/lock_screen/lib/ui/app_lock.dart | 4 +- .../lock_screen/lib/ui/lock_screen.dart | 6 +- .../lib/ui/lock_screen_auto_lock.dart | 2 +- .../lib/ui/lock_screen_confirm_password.dart | 2 +- .../lib/ui/lock_screen_confirm_pin.dart | 4 +- .../lib/ui/lock_screen_options.dart | 12 ++-- .../lib/ui/lock_screen_password.dart | 8 +-- .../lock_screen/lib/ui/lock_screen_pin.dart | 12 ++-- mobile/packages/lock_screen/pubspec.yaml | 6 +- 13 files changed, 109 insertions(+), 37 deletions(-) create mode 100644 mobile/packages/lock_screen/analysis_options.yaml diff --git a/mobile/packages/lock_screen/analysis_options.yaml b/mobile/packages/lock_screen/analysis_options.yaml new file mode 100644 index 0000000000..1bd78bc1b0 --- /dev/null +++ b/mobile/packages/lock_screen/analysis_options.yaml @@ -0,0 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + +include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index 396be4f809..f4071b0712 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -1,13 +1,13 @@ import 'dart:io'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index 6ae795c61e..eb1a65cf05 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -1,5 +1,10 @@ import 'dart:io'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen_password.dart'; +import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:flutter/foundation.dart'; @@ -7,11 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; -import 'package:ente_lock_screen/ui/lock_screen_password.dart'; -import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/lock_screen_settings.dart b/mobile/packages/lock_screen/lib/lock_screen_settings.dart index 99a41478bf..2c631bc931 100644 --- a/mobile/packages/lock_screen/lib/lock_screen_settings.dart +++ b/mobile/packages/lock_screen/lib/lock_screen_settings.dart @@ -3,10 +3,10 @@ import "dart:io"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; -import "package:ente_utils/platform_util.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:ente_events/event_bus.dart"; import "package:ente_events/models/signed_out_event.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; import "package:flutter_secure_storage/flutter_secure_storage.dart"; import "package:privacy_screen/privacy_screen.dart"; diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index f72fa1e8a5..9098737b7f 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:flutter/material.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). @@ -144,8 +144,8 @@ class _AppLockState extends State with WidgetsBindingObserver { Widget get _lockScreen { return PopScope( - child: this.widget.lockScreen, canPop: false, + child: this.widget.lockScreen, ); } diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index e89869b740..270d201eba 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -3,15 +3,15 @@ import 'dart:math'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index 1f0901ed50..16b5f45ffe 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -1,3 +1,4 @@ +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/captioned_text_widget.dart'; import 'package:ente_ui/components/divider_widget.dart'; @@ -7,7 +8,6 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index 716a1ad7da..eb9e8d6824 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -1,3 +1,4 @@ +import "package:ente_lock_screen/lock_screen_settings.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; @@ -5,7 +6,6 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index 33ce921012..ecb4418968 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -1,11 +1,11 @@ import "dart:io"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 2bf9d21131..176172af95 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -1,6 +1,12 @@ import "dart:async"; import "dart:io"; +import "package:ente_lock_screen/local_authentication_service.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/app_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_pin.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/models/button_type.dart"; @@ -14,12 +20,6 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; -import "package:ente_lock_screen/local_authentication_service.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/app_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index 93a1c4d636..eb5f2b94ed 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -1,16 +1,16 @@ import "dart:convert"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 2001cae468..603ebdd67d 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -1,17 +1,17 @@ import "dart:convert"; import "dart:io"; -import "package:ente_strings/ente_strings.dart"; -import "package:ente_ui/theme/colors.dart"; -import "package:ente_ui/theme/ente_theme.dart"; -import "package:ente_ui/theme/text_style.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; import "package:ente_lock_screen/lock_screen_settings.dart"; import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; import "package:ente_lock_screen/ui/lock_screen_options.dart"; +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/theme/colors.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_ui/theme/text_style.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index e5906cfc6a..5f6f1529c1 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -8,8 +8,6 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter ente_accounts: path: ../accounts ente_configuration: @@ -25,6 +23,8 @@ dependencies: path: ../ui ente_utils: path: ../utils + flutter: + sdk: flutter flutter_animate: ^4.1.0 flutter_local_authentication: git: @@ -38,8 +38,8 @@ dependencies: shared_preferences: ^2.5.3 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 2e3d62107789f4116b16d9e0f02eee2defbdaecf Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:38:15 +0530 Subject: [PATCH 046/164] Update strings --- .../lib/l10n/strings_localizations_sk.dart | 328 ++++++++++++++++++ .../lib/l10n/strings_localizations_zh.dart | 328 ++++++++++++++++++ 2 files changed, 656 insertions(+) diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index c61b186331..0ac667a6b9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -619,4 +619,332 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get sorryTheCodeYouveEnteredIsIncorrect => 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 61b0924d06..ef097b2199 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -618,6 +618,334 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get sorryTheCodeYouveEnteredIsIncorrect => 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From dbdf19ee8df3e0e51af0195fc82f426e580d16fc Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:01 +0530 Subject: [PATCH 047/164] Update common lockscreen --- mobile/packages/lock_screen/lib/auth_util.dart | 2 ++ .../lock_screen/lib/local_authentication_service.dart | 5 +++++ mobile/packages/lock_screen/lib/ui/app_lock.dart | 1 + mobile/packages/lock_screen/lib/ui/lock_screen.dart | 3 +++ .../packages/lock_screen/lib/ui/lock_screen_auto_lock.dart | 1 + .../lock_screen/lib/ui/lock_screen_confirm_password.dart | 1 + .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 2 ++ mobile/packages/lock_screen/lib/ui/lock_screen_options.dart | 6 ++++++ .../packages/lock_screen/lib/ui/lock_screen_password.dart | 3 +++ mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart | 4 ++++ 10 files changed, 28 insertions(+) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index f4071b0712..f529d507de 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -8,6 +8,8 @@ import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index eb1a65cf05..cb376120c9 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -12,6 +12,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen_password.dart'; +import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index 9098737b7f..2cf3daa915 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:flutter/material.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index 270d201eba..3d0d4f0847 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -12,6 +12,9 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index 16b5f45ffe..c584193c47 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -8,6 +8,7 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index eb9e8d6824..1fc07b8462 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -6,6 +6,7 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index ecb4418968..efa73b4b13 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -6,6 +6,8 @@ import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 176172af95..fb811f0e18 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -20,6 +20,12 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; +import "package:ente_lock_screen/local_authentication_service.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/app_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index eb5f2b94ed..f3d857f494 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -11,6 +11,9 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 603ebdd67d..5eedad5ab6 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -12,6 +12,10 @@ import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/theme/text_style.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. From 3b727549d5540ac69a4ce6fb87854bacf194136f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:09 +0530 Subject: [PATCH 048/164] Update common ui From 0b766415a44cd6f4b20e63ef3217d545c6bef6b0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 049/164] Setup common accounts package --- .../packages/accounts/analysis_options.yaml | 2 + .../packages/accounts/lib/ente_accounts.dart | 34 + .../accounts/lib/models/user_details.dart | 250 ++++ .../lib/pages/change_email_dialog.dart | 82 ++ .../lib/pages/delete_account_page.dart | 6 +- .../accounts/lib/pages/login_page.dart | 2 +- .../pages/login_pwd_verification_page.dart | 4 +- .../lib/pages/ott_verification_page.dart | 2 +- .../accounts/lib/pages/passkey_page.dart | 2 +- .../lib/pages/password_entry_page.dart | 6 +- .../lib/pages/password_reentry_page.dart | 5 +- .../accounts/lib/pages/recovery_key_page.dart | 2 +- .../accounts/lib/pages/recovery_page.dart | 2 +- .../pages/request_pwd_verification_page.dart | 4 +- .../accounts/lib/pages/sessions_page.dart | 4 +- .../pages/two_factor_authentication_page.dart | 4 +- .../lib/pages/two_factor_recovery_page.dart | 2 +- .../accounts/lib/services/user_service.dart | 12 +- mobile/packages/accounts/pubspec.lock | 1280 +++++++++++++++++ mobile/packages/accounts/pubspec.yaml | 63 + 20 files changed, 1739 insertions(+), 29 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index 1bd78bc1b0..26bd4fe060 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -70,3 +70,5 @@ analyzer: prefer_interpolation_to_compose_strings: ignore # later too many warnings prefer_double_quotes: ignore # too many warnings avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides + +include: package:flutter_lints/flutter.yaml diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index 9fcaf2bcf6..bf9c53cf67 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -26,3 +26,37 @@ export 'pages/two_factor_recovery_page.dart'; export 'services/passkey_service.dart'; export 'services/user_service.dart'; + +/// A Flutter package containing account-related functionality for Ente apps +library ente_accounts; + +// Models +export 'models/user_details.dart'; +export 'models/sessions.dart'; +export 'models/two_factor.dart'; +export 'models/srp.dart'; +export 'models/delete_account.dart'; +export 'models/set_keys_request.dart'; +export 'models/set_recovery_key_request.dart'; +export 'models/bonus.dart'; +export 'models/subscription.dart'; + +// Services +export 'services/user_service.dart'; + +// Pages +export 'pages/change_email_dialog.dart'; +export 'pages/delete_account_page.dart'; +export 'pages/email_entry_page.dart'; +export 'pages/login_page.dart'; +export 'pages/login_pwd_verification_page.dart'; +export 'pages/ott_verification_page.dart'; +export 'pages/passkey_page.dart'; +export 'pages/password_entry_page.dart'; +export 'pages/password_reentry_page.dart'; +export 'pages/recovery_key_page.dart'; +export 'pages/recovery_page.dart'; +export 'pages/request_pwd_verification_page.dart'; +export 'pages/sessions_page.dart'; +export 'pages/two_factor_authentication_page.dart'; +export 'pages/two_factor_recovery_page.dart'; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index 7fe5f8e60f..d7908a60de 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -247,3 +247,253 @@ class FamilyData { factory FamilyData.fromJson(String source) => FamilyData.fromMap(json.decode(source)); } + +import 'dart:convert'; +import 'dart:math'; + +import 'bonus.dart'; +import 'subscription.dart'; + +class UserDetails { + final String email; + final int usage; + final int fileCount; + final int storageBonus; + final int sharedCollectionsCount; + final Subscription subscription; + final FamilyData? familyData; + final ProfileData? profileData; + final BonusData? bonusData; + + const UserDetails( + this.email, + this.usage, + this.fileCount, + this.storageBonus, + this.sharedCollectionsCount, + this.subscription, + this.familyData, + this.profileData, + this.bonusData, + ); + + bool isPartOfFamily() { + return familyData?.members?.isNotEmpty ?? false; + } + + bool hasPaidAddon() { + return bonusData?.getAddOnBonuses().isNotEmpty ?? false; + } + + bool isFamilyAdmin() { + assert(isPartOfFamily(), "verify user is part of family before calling"); + final FamilyMember currentUserMember = familyData!.members! + .firstWhere((element) => element.email.trim() == email.trim()); + return currentUserMember.isAdmin; + } + + // getFamilyOrPersonalUsage will return total usage for family if user + // belong to family group. Otherwise, it will return storage consumed by + // current user + int getFamilyOrPersonalUsage() { + return isPartOfFamily() ? familyData!.getTotalUsage() : usage; + } + + int getFreeStorage() { + final int? memberLimit = familyMemberStorageLimit(); + if (memberLimit != null) { + return max(memberLimit - usage, 0); + } + return max(getTotalStorage() - getFamilyOrPersonalUsage(), 0); + } + + // getTotalStorage will return total storage available including the + // storage bonus + int getTotalStorage() { + return (isPartOfFamily() ? familyData!.storage : subscription.storage) + + storageBonus; + } + + // return the member storage limit if user is part of family and the admin + // has set the storage limit for the user. + int? familyMemberStorageLimit() { + if (isPartOfFamily()) { + try { + final FamilyMember currentUserMember = familyData!.members! + .firstWhere((element) => element.email.trim() == email.trim()); + return currentUserMember.storageLimit; + } catch (e) { + return null; + } + } + return null; + } + + // This is the total storage for which user has paid for. + int getPlanPlusAddonStorage() { + return (isPartOfFamily() ? familyData!.storage : subscription.storage) + + bonusData!.totalAddOnBonus(); + } + + factory UserDetails.fromMap(Map map) { + return UserDetails( + map['email'] as String, + map['usage'] as int, + (map['fileCount'] ?? 0) as int, + (map['storageBonus'] ?? 0) as int, + (map['sharedCollectionsCount'] ?? 0) as int, + Subscription.fromMap(map['subscription']), + FamilyData.fromMap(map['familyData']), + ProfileData.fromJson(map['profileData']), + BonusData.fromJson(map['bonusData']), + ); + } + + Map toMap() { + return { + 'email': email, + 'usage': usage, + 'fileCount': fileCount, + 'storageBonus': storageBonus, + 'sharedCollectionsCount': sharedCollectionsCount, + 'subscription': subscription.toMap(), + 'familyData': familyData?.toMap(), + 'profileData': profileData?.toJson(), + 'bonusData': bonusData?.toJson(), + }; + } + + String toJson() => json.encode(toMap()); + + factory UserDetails.fromJson(String source) => + UserDetails.fromMap(json.decode(source)); +} + +class FamilyMember { + final String email; + final int usage; + final String id; + final bool isAdmin; + final int? storageLimit; + + FamilyMember( + this.email, + this.usage, + this.id, + this.isAdmin, + this.storageLimit, + ); + + factory FamilyMember.fromMap(Map map) { + return FamilyMember( + (map['email'] ?? '') as String, + map['usage'] as int, + map['id'] as String, + map['isAdmin'] as bool, + map['storageLimit'] as int?, + ); + } + + Map toMap() { + return { + 'email': email, + 'usage': usage, + 'id': id, + 'isAdmin': isAdmin, + 'storageLimit': storageLimit, + }; + } + + String toJson() => json.encode(toMap()); + + factory FamilyMember.fromJson(String source) => + FamilyMember.fromMap(json.decode(source)); +} + +class ProfileData { + bool canDisableEmailMFA; + bool isEmailMFAEnabled; + bool isTwoFactorEnabled; + + // Constructor with default values + ProfileData({ + this.canDisableEmailMFA = false, + this.isEmailMFAEnabled = false, + this.isTwoFactorEnabled = false, + }); + + // Factory method to create ProfileData instance from JSON + factory ProfileData.fromJson(Map? json) { + return ProfileData( + canDisableEmailMFA: json?['canDisableEmailMFA'] ?? false, + isEmailMFAEnabled: json?['isEmailMFAEnabled'] ?? false, + isTwoFactorEnabled: json?['isTwoFactorEnabled'] ?? false, + ); + } + + // Method to convert ProfileData instance to JSON + Map toJson() { + return { + 'canDisableEmailMFA': canDisableEmailMFA, + 'isEmailMFAEnabled': isEmailMFAEnabled, + 'isTwoFactorEnabled': isTwoFactorEnabled, + }; + } + + String toJsonString() => json.encode(toJson()); +} + +class FamilyData { + final List? members; + + // Storage available based on the family plan + final int storage; + final int expiryTime; + + FamilyData( + this.members, + this.storage, + this.expiryTime, + ); + + int getTotalUsage() { + return members! + .map((e) => e.usage) + .toList() + .fold(0, (sum, usage) => sum + usage); + } + + FamilyMember? getMemberByID(String id) { + try { + return members!.firstWhere((element) => element.id == id); + } catch (e) { + return null; + } + } + + static fromMap(Map? map) { + if (map == null) return null; + assert(map['members'] != null && map['members'].length >= 0); + final members = List.from( + map['members'].map((x) => FamilyMember.fromMap(x)), + ); + return FamilyData( + members, + map['storage'] as int, + map['expiryTime'] as int, + ); + } + + Map toMap() { + return { + 'members': members?.map((x) => x.toMap()).toList(), + 'storage': storage, + 'expiryTime': expiryTime, + }; + } + + String toJson() => json.encode(toMap()); + + factory FamilyData.fromJson(String source) => + FamilyData.fromMap(json.decode(source)); +} diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index 860b627918..b9f8199873 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -79,3 +79,85 @@ class _ChangeEmailDialogState extends State { ); } } + +import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:flutter/material.dart'; + +class ChangeEmailDialog extends StatefulWidget { + const ChangeEmailDialog({super.key}); + + @override + State createState() => _ChangeEmailDialogState(); +} + +class _ChangeEmailDialogState extends State { + String _email = ""; + + @override + Widget build(BuildContext context) { + return AlertDialog( + title: Text(context.strings.enterNewEmailHint), + content: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextFormField( + decoration: InputDecoration( + hintText: context.strings.email, + hintStyle: const TextStyle( + color: Colors.white30, + ), + contentPadding: const EdgeInsets.all(12), + ), + onChanged: (value) { + setState(() { + _email = value; + }); + }, + autocorrect: false, + keyboardType: TextInputType.emailAddress, + initialValue: _email, + autofocus: true, + ), + ], + ), + ), + actions: [ + TextButton( + child: Text( + context.strings.cancel, + style: const TextStyle( + color: Colors.redAccent, + ), + ), + onPressed: () { + Navigator.pop(context); + }, + ), + TextButton( + child: Text( + context.strings.verify, + style: const TextStyle( + color: Colors.purple, + ), + ), + onPressed: () { + if (!isValidEmail(_email)) { + showErrorDialog( + context, + context.strings.invalidEmailTitle, + context.strings.invalidEmailMessage, + ); + return; + } + UserService.instance.sendOtt(context, _email, isChangeEmail: true); + }, + ), + ], + ); + } +} diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index 66963914d7..b06ac5e4d5 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -4,13 +4,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_ui/components/buttons/gradient_button.dart'; -import 'package:ente_ui/components/dialogs.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; class DeleteAccountPage extends StatelessWidget { final BaseConfiguration configuration; diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index c51b74e8a4..17737cc0e0 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -2,10 +2,10 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import "package:ente_strings/ente_strings.dart"; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; +import "package:ente_strings/ente_strings.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import "package:styled_text/styled_text.dart"; diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart index e23064d389..6c0144f3b0 100644 --- a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -1,13 +1,13 @@ import "package:dio/dio.dart"; import "package:ente_accounts/ente_accounts.dart"; import "package:ente_configuration/base_configuration.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; import "package:ente_utils/email_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart index cc49d1f88d..442401fbd7 100644 --- a/mobile/packages/accounts/lib/pages/ott_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; import 'package:styled_text/styled_text.dart'; diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart index f7470939da..584be1bceb 100644 --- a/mobile/packages/accounts/lib/pages/passkey_page.dart +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -4,12 +4,12 @@ import 'package:app_links/app_links.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index 36152d883b..bf171e3cc3 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -1,6 +1,5 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/pages/base_home_page.dart'; @@ -9,6 +8,7 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; @@ -448,9 +448,7 @@ class _PasswordEntryPageState extends State { bool usingVolatilePassword = false, }) async { final dialog = createProgressDialog( - context, - context.strings.generatingEncryptionKeysTitle, - ); + context, context.strings.generatingEncryptionKeysTitle); await dialog.show(); try { if (usingVolatilePassword) { diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 7e081c9a5c..0de41c1992 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -4,16 +4,17 @@ import 'dart:typed_data'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; import 'package:flutter/material.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:logging/logging.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; + class PasswordReentryPage extends StatefulWidget { final BaseHomePage homePage; final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 3cf2249771..20f49b8f52 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -5,12 +5,12 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:dotted_border/dotted_border.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index cc889bc34c..0901452a11 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart index d3d23a1096..0fa74243ad 100644 --- a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -2,11 +2,11 @@ import "dart:convert"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart index dbbcb9bf98..29774497ee 100644 --- a/mobile/packages/accounts/lib/pages/sessions_page.dart +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -1,13 +1,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/loading_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_utils/date_time_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:ente_utils/date_time_util.dart'; class SessionsPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart index f1fa4f327e..58a7312e5a 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_ui/lifecycle_event_handler.dart'; import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinput/pinput.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; class TwoFactorAuthenticationPage extends StatefulWidget { final String sessionID; diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart index 878a8a2ba8..12c6da59aa 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class TwoFactorRecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index f127fe50be..8af3d55973 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -20,19 +20,19 @@ import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_accounts/pages/recovery_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; import 'package:ente_network/network.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/progress_dialog.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 1dd9150ce5..a980de557a 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -1278,3 +1278,1283 @@ packages: sdks: dart: ">=3.8.0 <4.0.0" flutter: ">=3.29.0" + +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: "direct main" + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: "direct main" + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: df26af0de7c4eff46c53c190b5590e22457bfce6ea679aedb1e6326197f27d6f + url: "https://pub.dev" + source: hosted + version: "1.4.0" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + url: "https://pub.dev" + source: hosted + version: "7.0.3" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + dotted_border: + dependency: "direct main" + description: + name: dotted_border + sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + email_validator: + dependency: "direct main" + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_base: + dependency: "direct main" + description: + path: "../base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../events" + relative: true + source: path + version: "1.0.0" + ente_lock_screen: + dependency: "direct main" + description: + path: "../lock_screen" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: transitive + description: + path: "../logging" + relative: true + source: path + version: "1.0.0" + ente_network: + dependency: "direct main" + description: + path: "../network" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: "direct main" + description: + path: "../ui" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: "direct main" + description: + path: "../utils" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: transitive + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_saver: + dependency: "direct main" + description: + name: file_saver + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: transitive + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_email_sender: + dependency: transitive + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_local_authentication: + dependency: transitive + description: + name: flutter_local_authentication + sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_localizations: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + url: "https://pub.dev" + source: hosted + version: "2.0.28" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: transitive + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + intl: + dependency: transitive + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + jni: + dependency: transitive + description: + name: jni + sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 + url: "https://pub.dev" + source: hosted + version: "0.14.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + url: "https://pub.dev" + source: hosted + version: "5.1.1" + local_auth: + dependency: transitive + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + url: "https://pub.dev" + source: hosted + version: "1.0.50" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: transitive + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_dio_adapter: + dependency: transitive + description: + name: native_dio_adapter + sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: transitive + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + password_strength: + dependency: "direct main" + description: + name: password_strength + sha256: "0e51e3d864e37873a1347e658147f88b66e141ee36c58e19828dc5637961e1ce" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: transitive + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.dev" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pinput: + dependency: "direct main" + description: + name: pinput + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: "direct main" + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" + privacy_screen: + dependency: transitive + description: + name: privacy_screen + sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: "direct main" + description: + name: share_plus + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + step_progress_indicator: + dependency: "direct main" + description: + name: step_progress_indicator + sha256: b51bb1fcfc78454359f0658c5a2c21548c3825ebf76e826308e9ca10f383bbb8 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + styled_text: + dependency: "direct main" + description: + name: styled_text + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 + url: "https://pub.dev" + source: hosted + version: "8.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + ua_client_hints: + dependency: transitive + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + url: "https://pub.dev" + source: hosted + version: "6.3.2" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.dev" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: "direct main" + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + url: "https://pub.dev" + source: hosted + version: "5.14.0" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xmlstream: + dependency: transitive + description: + name: xmlstream + sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.29.0" diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index 1d4064ac75..584eb3bb2a 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -56,3 +56,66 @@ flutter: # This package is not meant to be published publish_to: none + +name: ente_accounts +description: A Flutter package containing account-related models, pages, and services for Ente apps +version: 1.0.0 + +environment: + sdk: ">=3.0.0 <4.0.0" + flutter: ">=1.17.0" + +dependencies: + flutter: + sdk: flutter + + # Ente packages + ente_strings: + path: ../strings + ente_ui: + path: ../ui + ente_utils: + path: ../utils + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_network: + path: ../network + ente_events: + path: ../events + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_lock_screen: + path: ../lock_screen + + # Third-party dependencies + bip39: ^1.0.6 + dio: ^5.4.0 + logging: ^1.2.0 + pointycastle: ^3.7.3 + shared_preferences: ^2.2.2 + file_saver: ^0.3.0 + share_plus: ^11.0.0 + uuid: ^4.2.1 + collection: ^1.18.0 + dotted_border: ^3.1.0 + password_strength: ^0.2.0 + step_progress_indicator: ^1.0.2 + styled_text: ^8.1.0 + email_validator: ^3.0.0 + pinput: ^5.0.1 + app_links: ^6.3.3 + url_launcher: ^6.3.1 + url_launcher_ios: ^6.3.1 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^5.0.0 + +flutter: + +# This package is not meant to be published +publish_to: none From d227a2bf20095d4419663145cc36ff7e50966df0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 050/164] Setup common accounts package --- .../packages/accounts/analysis_options.yaml | 73 - .../packages/accounts/lib/ente_accounts.dart | 29 - .../accounts/lib/models/user_details.dart | 250 ---- .../lib/pages/change_email_dialog.dart | 82 -- mobile/packages/accounts/pubspec.lock | 1281 ----------------- mobile/packages/accounts/pubspec.yaml | 59 - 6 files changed, 1774 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index 26bd4fe060..f9b303465f 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1,74 +1 @@ -# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html -# or https://pub.dev/packages/lint (Effective dart) -# use "flutter analyze ." or "dart analyze ." for running lint checks - -include: package:flutter_lints/flutter.yaml -linter: - rules: - # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml - # Ref https://dart-lang.github.io/linter/lints/ - - avoid_print - - avoid_unnecessary_containers - - avoid_web_libraries_in_flutter - - no_logic_in_create_state - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_final_locals - - require_trailing_commas - - sized_box_for_whitespace - - use_full_hex_values_for_flutter_colors - - use_key_in_widget_constructors - - cancel_subscriptions - - - - avoid_empty_else - - exhaustive_cases - - # just style suggestions - - sort_pub_dependencies - - use_rethrow_when_possible - - prefer_double_quotes - - directives_ordering - - always_use_package_imports - - sort_child_properties_last - - unawaited_futures - -analyzer: - errors: - avoid_empty_else: error - exhaustive_cases: error - curly_braces_in_flow_control_structures: error - directives_ordering: error - require_trailing_commas: error - always_use_package_imports: warning - prefer_final_fields: error - unused_import: error - camel_case_types: error - prefer_is_empty: warning - use_rethrow_when_possible: info - unused_field: warning - use_key_in_widget_constructors: warning - sort_child_properties_last: warning - sort_pub_dependencies: warning - library_private_types_in_public_api: warning - constant_identifier_names: ignore - prefer_const_constructors: warning - prefer_const_declarations: warning - prefer_const_constructors_in_immutables: warning - prefer_final_locals: warning - unnecessary_const: error - cancel_subscriptions: error - unrelated_type_equality_checks: error - unnecessary_cast: info - - - unawaited_futures: warning # convert to warning after fixing existing issues - invalid_dependency: info - use_build_context_synchronously: ignore # experimental lint, requires many changes - prefer_interpolation_to_compose_strings: ignore # later too many warnings - prefer_double_quotes: ignore # too many warnings - avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides - include: package:flutter_lints/flutter.yaml diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index bf9c53cf67..e7865b2f1b 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,32 +1,3 @@ -export 'models/bonus.dart'; -export 'models/delete_account.dart'; -export 'models/sessions.dart'; -export 'models/set_keys_request.dart'; -export 'models/set_recovery_key_request.dart'; -export 'models/srp.dart'; -export 'models/subscription.dart'; -export 'models/two_factor.dart'; -export 'models/user_details.dart'; - -export 'pages/change_email_dialog.dart'; -export 'pages/delete_account_page.dart'; -export 'pages/email_entry_page.dart'; -export 'pages/login_page.dart'; -export 'pages/login_pwd_verification_page.dart'; -export 'pages/ott_verification_page.dart'; -export 'pages/passkey_page.dart'; -export 'pages/password_entry_page.dart'; -export 'pages/password_reentry_page.dart'; -export 'pages/recovery_key_page.dart'; -export 'pages/recovery_page.dart'; -export 'pages/request_pwd_verification_page.dart'; -export 'pages/sessions_page.dart'; -export 'pages/two_factor_authentication_page.dart'; -export 'pages/two_factor_recovery_page.dart'; - -export 'services/passkey_service.dart'; -export 'services/user_service.dart'; - /// A Flutter package containing account-related functionality for Ente apps library ente_accounts; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index d7908a60de..bae0311a7f 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -1,256 +1,6 @@ import 'dart:convert'; import 'dart:math'; -import 'package:ente_accounts/models/bonus.dart'; -import 'package:ente_accounts/models/subscription.dart'; - -class UserDetails { - final String email; - final int usage; - final int fileCount; - final int storageBonus; - final int sharedCollectionsCount; - final Subscription subscription; - final FamilyData? familyData; - final ProfileData? profileData; - final BonusData? bonusData; - - const UserDetails( - this.email, - this.usage, - this.fileCount, - this.storageBonus, - this.sharedCollectionsCount, - this.subscription, - this.familyData, - this.profileData, - this.bonusData, - ); - - bool isPartOfFamily() { - return familyData?.members?.isNotEmpty ?? false; - } - - bool hasPaidAddon() { - return bonusData?.getAddOnBonuses().isNotEmpty ?? false; - } - - bool isFamilyAdmin() { - assert(isPartOfFamily(), "verify user is part of family before calling"); - final FamilyMember currentUserMember = familyData!.members! - .firstWhere((element) => element.email.trim() == email.trim()); - return currentUserMember.isAdmin; - } - - // getFamilyOrPersonalUsage will return total usage for family if user - // belong to family group. Otherwise, it will return storage consumed by - // current user - int getFamilyOrPersonalUsage() { - return isPartOfFamily() ? familyData!.getTotalUsage() : usage; - } - - int getFreeStorage() { - final int? memberLimit = familyMemberStorageLimit(); - if (memberLimit != null) { - return max(memberLimit - usage, 0); - } - return max(getTotalStorage() - getFamilyOrPersonalUsage(), 0); - } - - // getTotalStorage will return total storage available including the - // storage bonus - int getTotalStorage() { - return (isPartOfFamily() ? familyData!.storage : subscription.storage) + - storageBonus; - } - - // return the member storage limit if user is part of family and the admin - // has set the storage limit for the user. - int? familyMemberStorageLimit() { - if (isPartOfFamily()) { - try { - final FamilyMember currentUserMember = familyData!.members! - .firstWhere((element) => element.email.trim() == email.trim()); - return currentUserMember.storageLimit; - } catch (e) { - return null; - } - } - return null; - } - - // This is the total storage for which user has paid for. - int getPlanPlusAddonStorage() { - return (isPartOfFamily() ? familyData!.storage : subscription.storage) + - bonusData!.totalAddOnBonus(); - } - - factory UserDetails.fromMap(Map map) { - return UserDetails( - map['email'] as String, - map['usage'] as int, - (map['fileCount'] ?? 0) as int, - (map['storageBonus'] ?? 0) as int, - (map['sharedCollectionsCount'] ?? 0) as int, - Subscription.fromMap(map['subscription']), - FamilyData.fromMap(map['familyData']), - ProfileData.fromJson(map['profileData']), - BonusData.fromJson(map['bonusData']), - ); - } - - Map toMap() { - return { - 'email': email, - 'usage': usage, - 'fileCount': fileCount, - 'storageBonus': storageBonus, - 'sharedCollectionsCount': sharedCollectionsCount, - 'subscription': subscription.toMap(), - 'familyData': familyData?.toMap(), - 'profileData': profileData?.toJson(), - 'bonusData': bonusData?.toJson(), - }; - } - - String toJson() => json.encode(toMap()); - - factory UserDetails.fromJson(String source) => - UserDetails.fromMap(json.decode(source)); -} - -class FamilyMember { - final String email; - final int usage; - final String id; - final bool isAdmin; - final int? storageLimit; - - FamilyMember( - this.email, - this.usage, - this.id, - this.isAdmin, - this.storageLimit, - ); - - factory FamilyMember.fromMap(Map map) { - return FamilyMember( - (map['email'] ?? '') as String, - map['usage'] as int, - map['id'] as String, - map['isAdmin'] as bool, - map['storageLimit'] as int?, - ); - } - - Map toMap() { - return { - 'email': email, - 'usage': usage, - 'id': id, - 'isAdmin': isAdmin, - 'storageLimit': storageLimit, - }; - } - - String toJson() => json.encode(toMap()); - - factory FamilyMember.fromJson(String source) => - FamilyMember.fromMap(json.decode(source)); -} - -class ProfileData { - bool canDisableEmailMFA; - bool isEmailMFAEnabled; - bool isTwoFactorEnabled; - - // Constructor with default values - ProfileData({ - this.canDisableEmailMFA = false, - this.isEmailMFAEnabled = false, - this.isTwoFactorEnabled = false, - }); - - // Factory method to create ProfileData instance from JSON - factory ProfileData.fromJson(Map? json) { - return ProfileData( - canDisableEmailMFA: json?['canDisableEmailMFA'] ?? false, - isEmailMFAEnabled: json?['isEmailMFAEnabled'] ?? false, - isTwoFactorEnabled: json?['isTwoFactorEnabled'] ?? false, - ); - } - - // Method to convert ProfileData instance to JSON - Map toJson() { - return { - 'canDisableEmailMFA': canDisableEmailMFA, - 'isEmailMFAEnabled': isEmailMFAEnabled, - 'isTwoFactorEnabled': isTwoFactorEnabled, - }; - } - - String toJsonString() => json.encode(toJson()); -} - -class FamilyData { - final List? members; - - // Storage available based on the family plan - final int storage; - final int expiryTime; - - FamilyData( - this.members, - this.storage, - this.expiryTime, - ); - - int getTotalUsage() { - return members! - .map((e) => e.usage) - .toList() - .fold(0, (sum, usage) => sum + usage); - } - - FamilyMember? getMemberByID(String id) { - try { - return members!.firstWhere((element) => element.id == id); - } catch (e) { - return null; - } - } - - static fromMap(Map? map) { - if (map == null) return null; - assert(map['members'] != null && map['members'].length >= 0); - final members = List.from( - map['members'].map((x) => FamilyMember.fromMap(x)), - ); - return FamilyData( - members, - map['storage'] as int, - map['expiryTime'] as int, - ); - } - - Map toMap() { - return { - 'members': members?.map((x) => x.toMap()).toList(), - 'storage': storage, - 'expiryTime': expiryTime, - }; - } - - String toJson() => json.encode(toMap()); - - factory FamilyData.fromJson(String source) => - FamilyData.fromMap(json.decode(source)); -} - -import 'dart:convert'; -import 'dart:math'; - import 'bonus.dart'; import 'subscription.dart'; diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index b9f8199873..0cc49f25bc 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -1,85 +1,3 @@ -import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_ui/utils/dialog_util.dart'; -import 'package:ente_utils/email_util.dart'; -import 'package:flutter/material.dart'; - -class ChangeEmailDialog extends StatefulWidget { - const ChangeEmailDialog({super.key}); - - @override - State createState() => _ChangeEmailDialogState(); -} - -class _ChangeEmailDialogState extends State { - String _email = ""; - - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text(context.strings.enterNewEmailHint), - content: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - decoration: InputDecoration( - hintText: context.strings.email, - hintStyle: const TextStyle( - color: Colors.white30, - ), - contentPadding: const EdgeInsets.all(12), - ), - onChanged: (value) { - setState(() { - _email = value; - }); - }, - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: _email, - autofocus: true, - ), - ], - ), - ), - actions: [ - TextButton( - child: Text( - context.strings.cancel, - style: const TextStyle( - color: Colors.redAccent, - ), - ), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text( - context.strings.verify, - style: const TextStyle( - color: Colors.purple, - ), - ), - onPressed: () { - if (!isValidEmail(_email)) { - showErrorDialog( - context, - context.strings.invalidEmailTitle, - context.strings.invalidEmailMessage, - ); - return; - } - UserService.instance.sendOtt(context, _email, isChangeEmail: true); - }, - ), - ], - ); - } -} - import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index a980de557a..8fee60bb47 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -406,1287 +406,6 @@ packages: url: "https://pub.dev" source: hosted version: "5.0.0" - flutter_local_authentication: - dependency: transitive - description: - path: "." - ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - url: "https://github.com/eaceto/flutter_local_authentication" - source: git - version: "1.2.0" - flutter_localizations: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e - url: "https://pub.dev" - source: hosted - version: "2.0.28" - flutter_secure_storage: - dependency: transitive - description: - name: flutter_secure_storage - sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" - url: "https://pub.dev" - source: hosted - version: "9.2.4" - flutter_secure_storage_linux: - dependency: transitive - description: - name: flutter_secure_storage_linux - sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 - url: "https://pub.dev" - source: hosted - version: "1.2.3" - flutter_secure_storage_macos: - dependency: transitive - description: - name: flutter_secure_storage_macos - sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" - url: "https://pub.dev" - source: hosted - version: "3.1.3" - flutter_secure_storage_platform_interface: - dependency: transitive - description: - name: flutter_secure_storage_platform_interface - sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_secure_storage_web: - dependency: transitive - description: - name: flutter_secure_storage_web - sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - flutter_secure_storage_windows: - dependency: transitive - description: - name: flutter_secure_storage_windows - sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - flutter_shaders: - dependency: transitive - description: - name: flutter_shaders - sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" - url: "https://pub.dev" - source: hosted - version: "0.1.3" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - fluttertoast: - dependency: transitive - description: - name: fluttertoast - sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" - url: "https://pub.dev" - source: hosted - version: "8.2.12" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 - url: "https://pub.dev" - source: hosted - version: "2.4.4" - gtk: - dependency: transitive - description: - name: gtk - sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c - url: "https://pub.dev" - source: hosted - version: "2.1.0" - hex: - dependency: transitive - description: - name: hex - sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - html: - dependency: transitive - description: - name: html - sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" - source: hosted - version: "0.15.6" - http: - dependency: transitive - description: - name: http - sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" - source: hosted - version: "4.1.2" - http_profile: - dependency: transitive - description: - name: http_profile - sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - intl: - dependency: transitive - description: - name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" - source: hosted - version: "0.20.2" - jni: - dependency: transitive - description: - name: jni - sha256: d2c361082d554d4593c3012e26f6b188f902acd291330f13d6427641a92b3da1 - url: "https://pub.dev" - source: hosted - version: "0.14.2" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" - url: "https://pub.dev" - source: hosted - version: "10.0.9" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.dev" - source: hosted - version: "3.0.9" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.dev" - source: hosted - version: "5.1.1" - local_auth: - dependency: transitive - description: - name: local_auth - sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" - url: "https://pub.dev" - source: hosted - version: "2.3.0" - local_auth_android: - dependency: transitive - description: - name: local_auth_android - sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" - url: "https://pub.dev" - source: hosted - version: "1.0.50" - local_auth_darwin: - dependency: transitive - description: - name: local_auth_darwin - sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - local_auth_platform_interface: - dependency: transitive - description: - name: local_auth_platform_interface - sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" - url: "https://pub.dev" - source: hosted - version: "1.0.10" - local_auth_windows: - dependency: transitive - description: - name: local_auth_windows - sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 - url: "https://pub.dev" - source: hosted - version: "1.0.11" - logging: - dependency: "direct main" - description: - name: logging - sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - matcher: - dependency: transitive - description: - name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" - source: hosted - version: "0.12.17" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" - source: hosted - version: "0.11.1" - meta: - dependency: transitive - description: - name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" - source: hosted - version: "1.16.0" - mime: - dependency: transitive - description: - name: mime - sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - modal_bottom_sheet: - dependency: transitive - description: - name: modal_bottom_sheet - sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e - url: "https://pub.dev" - source: hosted - version: "3.0.0" - native_dio_adapter: - dependency: transitive - description: - name: native_dio_adapter - sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" - url: "https://pub.dev" - source: hosted - version: "1.4.0" - objective_c: - dependency: transitive - description: - name: objective_c - sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" - url: "https://pub.dev" - source: hosted - version: "7.1.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" - source: hosted - version: "2.2.0" - package_info_plus: - dependency: transitive - description: - name: package_info_plus - sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" - url: "https://pub.dev" - source: hosted - version: "8.3.0" - package_info_plus_platform_interface: - dependency: transitive - description: - name: package_info_plus_platform_interface - sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" - url: "https://pub.dev" - source: hosted - version: "3.2.0" - password_strength: - dependency: "direct main" - description: - name: password_strength - sha256: "0e51e3d864e37873a1347e658147f88b66e141ee36c58e19828dc5637961e1ce" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - path: - dependency: transitive - description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" - source: hosted - version: "1.9.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" - source: hosted - version: "2.1.5" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 - url: "https://pub.dev" - source: hosted - version: "2.2.17" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" - source: hosted - version: "2.3.0" - pinput: - dependency: "direct main" - description: - name: pinput - sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" - url: "https://pub.dev" - source: hosted - version: "5.0.1" - platform: - dependency: transitive - description: - name: platform - sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" - source: hosted - version: "3.1.6" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pointycastle: - dependency: "direct main" - description: - name: pointycastle - sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" - url: "https://pub.dev" - source: hosted - version: "3.9.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" - privacy_screen: - dependency: transitive - description: - name: privacy_screen - sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" - url: "https://pub.dev" - source: hosted - version: "0.0.8" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - screen_retriever: - dependency: transitive - description: - name: screen_retriever - sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_linux: - dependency: transitive - description: - name: screen_retriever_linux - sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_macos: - dependency: transitive - description: - name: screen_retriever_macos - sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_platform_interface: - dependency: transitive - description: - name: screen_retriever_platform_interface - sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 - url: "https://pub.dev" - source: hosted - version: "0.2.0" - screen_retriever_windows: - dependency: transitive - description: - name: screen_retriever_windows - sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" - url: "https://pub.dev" - source: hosted - version: "0.2.0" - sentry: - dependency: transitive - description: - name: sentry - sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" - url: "https://pub.dev" - source: hosted - version: "8.14.2" - sentry_flutter: - dependency: transitive - description: - name: sentry_flutter - sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" - url: "https://pub.dev" - source: hosted - version: "8.14.2" - share_plus: - dependency: "direct main" - description: - name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 - url: "https://pub.dev" - source: hosted - version: "11.0.0" - share_plus_platform_interface: - dependency: transitive - description: - name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" - url: "https://pub.dev" - source: hosted - version: "6.0.0" - shared_preferences: - dependency: "direct main" - description: - name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" - url: "https://pub.dev" - source: hosted - version: "2.5.3" - shared_preferences_android: - dependency: transitive - description: - name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" - url: "https://pub.dev" - source: hosted - version: "2.4.10" - shared_preferences_foundation: - dependency: transitive - description: - name: shared_preferences_foundation - sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - shared_preferences_linux: - dependency: transitive - description: - name: shared_preferences_linux - sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_platform_interface: - dependency: transitive - description: - name: shared_preferences_platform_interface - sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - shared_preferences_web: - dependency: transitive - description: - name: shared_preferences_web - sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 - url: "https://pub.dev" - source: hosted - version: "2.4.3" - shared_preferences_windows: - dependency: transitive - description: - name: shared_preferences_windows - sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - sodium: - dependency: transitive - description: - name: sodium - sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 - url: "https://pub.dev" - source: hosted - version: "2.3.1+1" - sodium_libs: - dependency: transitive - description: - name: sodium_libs - sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 - url: "https://pub.dev" - source: hosted - version: "2.2.1+6" - source_span: - dependency: transitive - description: - name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" - source: hosted - version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" - source: hosted - version: "1.12.1" - step_progress_indicator: - dependency: "direct main" - description: - name: step_progress_indicator - sha256: b51bb1fcfc78454359f0658c5a2c21548c3825ebf76e826308e9ca10f383bbb8 - url: "https://pub.dev" - source: hosted - version: "1.0.2" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - styled_text: - dependency: "direct main" - description: - name: styled_text - sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 - url: "https://pub.dev" - source: hosted - version: "8.1.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 - url: "https://pub.dev" - source: hosted - version: "3.4.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" - source: hosted - version: "1.2.2" - test_api: - dependency: transitive - description: - name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd - url: "https://pub.dev" - source: hosted - version: "0.7.4" - tuple: - dependency: transitive - description: - name: tuple - sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 - url: "https://pub.dev" - source: hosted - version: "2.0.2" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - ua_client_hints: - dependency: transitive - description: - name: ua_client_hints - sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" - url: "https://pub.dev" - source: hosted - version: "1.4.1" - universal_platform: - dependency: transitive - description: - name: universal_platform - sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 - url: "https://pub.dev" - source: hosted - version: "6.3.2" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" - url: "https://pub.dev" - source: hosted - version: "6.3.16" - url_launcher_ios: - dependency: "direct main" - description: - name: url_launcher_ios - sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" - url: "https://pub.dev" - source: hosted - version: "6.3.3" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" - url: "https://pub.dev" - source: hosted - version: "3.2.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" - url: "https://pub.dev" - source: hosted - version: "3.2.2" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - uuid: - dependency: "direct main" - description: - name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff - url: "https://pub.dev" - source: hosted - version: "4.5.1" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 - url: "https://pub.dev" - source: hosted - version: "15.0.0" - web: - dependency: transitive - description: - name: web - sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" - source: hosted - version: "1.1.1" - web_socket: - dependency: transitive - description: - name: web_socket - sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" - source: hosted - version: "1.0.1" - win32: - dependency: transitive - description: - name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" - url: "https://pub.dev" - source: hosted - version: "5.14.0" - win32_registry: - dependency: transitive - description: - name: win32_registry - sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" - url: "https://pub.dev" - source: hosted - version: "1.1.5" - window_manager: - dependency: transitive - description: - name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - xmlstream: - dependency: transitive - description: - name: xmlstream - sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 - url: "https://pub.dev" - source: hosted - version: "1.1.1" -sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" - -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - app_links: - dependency: "direct main" - description: - name: app_links - sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" - url: "https://pub.dev" - source: hosted - version: "6.4.0" - app_links_linux: - dependency: transitive - description: - name: app_links_linux - sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 - url: "https://pub.dev" - source: hosted - version: "1.0.3" - app_links_platform_interface: - dependency: transitive - description: - name: app_links_platform_interface - sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - app_links_web: - dependency: transitive - description: - name: app_links_web - sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 - url: "https://pub.dev" - source: hosted - version: "1.0.4" - archive: - dependency: transitive - description: - name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" - url: "https://pub.dev" - source: hosted - version: "4.0.7" - args: - dependency: transitive - description: - name: args - sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" - source: hosted - version: "2.7.0" - async: - dependency: transitive - description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" - source: hosted - version: "2.13.0" - bip39: - dependency: "direct main" - description: - name: bip39 - sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc - url: "https://pub.dev" - source: hosted - version: "1.0.6" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - characters: - dependency: transitive - description: - name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" - source: hosted - version: "1.4.0" - clock: - dependency: transitive - description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" - source: hosted - version: "1.1.2" - collection: - dependency: "direct main" - description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" - source: hosted - version: "1.19.1" - convert: - dependency: transitive - description: - name: convert - sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" - source: hosted - version: "3.1.2" - cronet_http: - dependency: transitive - description: - name: cronet_http - sha256: df26af0de7c4eff46c53c190b5590e22457bfce6ea679aedb1e6326197f27d6f - url: "https://pub.dev" - source: hosted - version: "1.4.0" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" - url: "https://pub.dev" - source: hosted - version: "0.3.4+2" - crypto: - dependency: transitive - description: - name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" - url: "https://pub.dev" - source: hosted - version: "3.0.6" - csslib: - dependency: transitive - description: - name: csslib - sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" - source: hosted - version: "1.0.2" - cupertino_http: - dependency: transitive - description: - name: cupertino_http - sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" - url: "https://pub.dev" - source: hosted - version: "2.2.0" - device_info_plus: - dependency: transitive - description: - name: device_info_plus - sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" - url: "https://pub.dev" - source: hosted - version: "9.1.2" - device_info_plus_platform_interface: - dependency: transitive - description: - name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f - url: "https://pub.dev" - source: hosted - version: "7.0.3" - dio: - dependency: "direct main" - description: - name: dio - sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" - url: "https://pub.dev" - source: hosted - version: "5.8.0+1" - dio_web_adapter: - dependency: transitive - description: - name: dio_web_adapter - sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - dotted_border: - dependency: "direct main" - description: - name: dotted_border - sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" - url: "https://pub.dev" - source: hosted - version: "3.1.0" - email_validator: - dependency: "direct main" - description: - name: email_validator - sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb - url: "https://pub.dev" - source: hosted - version: "3.0.0" - ente_base: - dependency: "direct main" - description: - path: "../base" - relative: true - source: path - version: "1.0.0" - ente_configuration: - dependency: "direct main" - description: - path: "../configuration" - relative: true - source: path - version: "1.0.0" - ente_crypto_dart: - dependency: "direct main" - description: - path: "." - ref: HEAD - resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 - url: "https://github.com/ente-io/ente_crypto_dart.git" - source: git - version: "1.0.0" - ente_events: - dependency: "direct main" - description: - path: "../events" - relative: true - source: path - version: "1.0.0" - ente_lock_screen: - dependency: "direct main" - description: - path: "../lock_screen" - relative: true - source: path - version: "1.0.0" - ente_logging: - dependency: transitive - description: - path: "../logging" - relative: true - source: path - version: "1.0.0" - ente_network: - dependency: "direct main" - description: - path: "../network" - relative: true - source: path - version: "1.0.0" - ente_strings: - dependency: "direct main" - description: - path: "../strings" - relative: true - source: path - version: "1.0.0" - ente_ui: - dependency: "direct main" - description: - path: "../ui" - relative: true - source: path - version: "1.0.0" - ente_utils: - dependency: "direct main" - description: - path: "../utils" - relative: true - source: path - version: "1.0.0" - event_bus: - dependency: transitive - description: - name: event_bus - sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" - url: "https://pub.dev" - source: hosted - version: "2.0.1" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" - source: hosted - version: "1.3.3" - ffi: - dependency: transitive - description: - name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - file: - dependency: transitive - description: - name: file - sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" - source: hosted - version: "7.0.1" - file_saver: - dependency: "direct main" - description: - name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" - url: "https://pub.dev" - source: hosted - version: "0.3.1" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" - source: hosted - version: "1.1.1" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_animate: - dependency: transitive - description: - name: flutter_animate - sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" - url: "https://pub.dev" - source: hosted - version: "4.5.2" - flutter_email_sender: - dependency: transitive - description: - name: flutter_email_sender - sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c - url: "https://pub.dev" - source: hosted - version: "7.0.0" - flutter_inappwebview: - dependency: transitive - description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" - flutter_inappwebview_android: - dependency: transitive - description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" - flutter_inappwebview_internal_annotations: - dependency: transitive - description: - name: flutter_inappwebview_internal_annotations - sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - flutter_inappwebview_ios: - dependency: transitive - description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_inappwebview_macos: - dependency: transitive - description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_inappwebview_platform_interface: - dependency: transitive - description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" - flutter_inappwebview_web: - dependency: transitive - description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" - flutter_inappwebview_windows: - dependency: transitive - description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.dev" - source: hosted - version: "5.0.0" flutter_local_authentication: dependency: transitive description: diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index 584eb3bb2a..eca03a2a01 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -2,65 +2,6 @@ name: ente_accounts description: A Flutter package containing account-related models, pages, and services for Ente apps version: 1.0.0 -environment: - sdk: ">=3.0.0 <4.0.0" - flutter: ">=1.17.0" - -dependencies: - app_links: ^6.3.3 - bip39: ^1.0.6 - collection: ^1.18.0 - dio: ^5.4.0 - dotted_border: ^3.1.0 - email_validator: ^3.0.0 - ente_base: - path: ../base - ente_configuration: - path: ../configuration - ente_crypto_dart: - git: - url: https://github.com/ente-io/ente_crypto_dart.git - ente_events: - path: ../events - ente_lock_screen: - path: ../lock_screen - ente_network: - path: ../network - ente_strings: - path: ../strings - ente_ui: - path: ../ui - ente_utils: - path: ../utils - file_saver: ^0.3.0 - flutter: - sdk: flutter - logging: ^1.2.0 - password_strength: ^0.2.0 - pinput: ^5.0.1 - pointycastle: ^3.7.3 - share_plus: ^11.0.0 - shared_preferences: ^2.2.2 - step_progress_indicator: ^1.0.2 - styled_text: ^8.1.0 - url_launcher: ^6.3.1 - url_launcher_ios: ^6.3.1 - uuid: ^4.2.1 - -dev_dependencies: - flutter_lints: ^5.0.0 - flutter_test: - sdk: flutter - -flutter: - -# This package is not meant to be published -publish_to: none - -name: ente_accounts -description: A Flutter package containing account-related models, pages, and services for Ente apps -version: 1.0.0 - environment: sdk: ">=3.0.0 <4.0.0" flutter: ">=1.17.0" From 39b9670fcc7d8334b722374f86780e27622df16d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 051/164] Setup common accounts package From 827090fb24c56ebdf74ae8a8f2098808f7d1bb59 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:14:45 +0530 Subject: [PATCH 052/164] Fix dependency From 77cb40aef4029dbe96711fbaf6985ef0fc31221f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:21:00 +0530 Subject: [PATCH 053/164] Update strings From e8551df8b99ade255b9c77b4508d460743767abd Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:44:06 +0530 Subject: [PATCH 054/164] Remove changelog From a4626ae7a1e644f7a8f15b0aa769293bf100581a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:02:39 +0530 Subject: [PATCH 055/164] Remove noise From bbac09b4a6c750f74eafd83e7ff2cc180cac2afa Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:12:57 +0530 Subject: [PATCH 056/164] Lint accounts --- .../packages/accounts/analysis_options.yaml | 71 +++++++++++++++++++ .../packages/accounts/lib/ente_accounts.dart | 18 +++-- .../accounts/lib/models/user_details.dart | 4 +- .../lib/pages/change_email_dialog.dart | 2 +- .../lib/pages/delete_account_page.dart | 6 +- .../accounts/lib/pages/login_page.dart | 2 +- .../pages/login_pwd_verification_page.dart | 4 +- .../lib/pages/ott_verification_page.dart | 2 +- .../accounts/lib/pages/passkey_page.dart | 2 +- .../lib/pages/password_entry_page.dart | 6 +- .../lib/pages/password_reentry_page.dart | 5 +- .../accounts/lib/pages/recovery_key_page.dart | 2 +- .../accounts/lib/pages/recovery_page.dart | 2 +- .../pages/request_pwd_verification_page.dart | 4 +- .../accounts/lib/pages/sessions_page.dart | 4 +- .../pages/two_factor_authentication_page.dart | 4 +- .../lib/pages/two_factor_recovery_page.dart | 2 +- .../accounts/lib/services/user_service.dart | 12 ++-- mobile/packages/accounts/pubspec.lock | 9 +-- mobile/packages/accounts/pubspec.yaml | 60 ++++++++-------- 20 files changed, 144 insertions(+), 77 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index f9b303465f..1bd78bc1b0 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index e7865b2f1b..858677e3f9 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,21 +1,16 @@ /// A Flutter package containing account-related functionality for Ente apps library ente_accounts; -// Models -export 'models/user_details.dart'; -export 'models/sessions.dart'; -export 'models/two_factor.dart'; -export 'models/srp.dart'; +export 'models/bonus.dart'; export 'models/delete_account.dart'; +export 'models/sessions.dart'; export 'models/set_keys_request.dart'; export 'models/set_recovery_key_request.dart'; -export 'models/bonus.dart'; +export 'models/srp.dart'; export 'models/subscription.dart'; +export 'models/two_factor.dart'; +export 'models/user_details.dart'; -// Services -export 'services/user_service.dart'; - -// Pages export 'pages/change_email_dialog.dart'; export 'pages/delete_account_page.dart'; export 'pages/email_entry_page.dart'; @@ -31,3 +26,6 @@ export 'pages/request_pwd_verification_page.dart'; export 'pages/sessions_page.dart'; export 'pages/two_factor_authentication_page.dart'; export 'pages/two_factor_recovery_page.dart'; + +export 'services/passkey_service.dart'; +export 'services/user_service.dart'; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index bae0311a7f..7fe5f8e60f 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:math'; -import 'bonus.dart'; -import 'subscription.dart'; +import 'package:ente_accounts/models/bonus.dart'; +import 'package:ente_accounts/models/subscription.dart'; class UserDetails { final String email; diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index 0cc49f25bc..860b627918 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class ChangeEmailDialog extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index b06ac5e4d5..66963914d7 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -4,13 +4,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; -import 'package:ente_ui/components/buttons/gradient_button.dart'; -import 'package:ente_ui/components/dialogs.dart'; class DeleteAccountPage extends StatelessWidget { final BaseConfiguration configuration; diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index 17737cc0e0..c51b74e8a4 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -2,10 +2,10 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import "package:ente_strings/ente_strings.dart"; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; -import "package:ente_strings/ente_strings.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import "package:styled_text/styled_text.dart"; diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart index 6c0144f3b0..e23064d389 100644 --- a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -1,13 +1,13 @@ import "package:dio/dio.dart"; import "package:ente_accounts/ente_accounts.dart"; import "package:ente_configuration/base_configuration.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; import "package:ente_utils/email_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart index 442401fbd7..cc49d1f88d 100644 --- a/mobile/packages/accounts/lib/pages/ott_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; import 'package:styled_text/styled_text.dart'; diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart index 584be1bceb..f7470939da 100644 --- a/mobile/packages/accounts/lib/pages/passkey_page.dart +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -4,12 +4,12 @@ import 'package:app_links/app_links.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index bf171e3cc3..36152d883b 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -1,5 +1,6 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/pages/base_home_page.dart'; @@ -8,7 +9,6 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; @@ -448,7 +448,9 @@ class _PasswordEntryPageState extends State { bool usingVolatilePassword = false, }) async { final dialog = createProgressDialog( - context, context.strings.generatingEncryptionKeysTitle); + context, + context.strings.generatingEncryptionKeysTitle, + ); await dialog.show(); try { if (usingVolatilePassword) { diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 0de41c1992..7e081c9a5c 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -4,17 +4,16 @@ import 'dart:typed_data'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; import 'package:flutter/material.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:logging/logging.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; - class PasswordReentryPage extends StatefulWidget { final BaseHomePage homePage; final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 20f49b8f52..3cf2249771 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -5,12 +5,12 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:dotted_border/dotted_border.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index 0901452a11..cc889bc34c 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart index 0fa74243ad..d3d23a1096 100644 --- a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -2,11 +2,11 @@ import "dart:convert"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart index 29774497ee..dbbcb9bf98 100644 --- a/mobile/packages/accounts/lib/pages/sessions_page.dart +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -1,13 +1,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/loading_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_utils/date_time_util.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:ente_utils/date_time_util.dart'; class SessionsPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart index 58a7312e5a..f1fa4f327e 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinput/pinput.dart'; -import 'package:ente_ui/lifecycle_event_handler.dart'; class TwoFactorAuthenticationPage extends StatefulWidget { final String sessionID; diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart index 12c6da59aa..878a8a2ba8 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class TwoFactorRecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index 8af3d55973..f127fe50be 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -20,19 +20,19 @@ import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_accounts/pages/recovery_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; import 'package:ente_network/network.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/progress_dialog.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 8fee60bb47..1dd9150ce5 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -409,10 +409,11 @@ packages: flutter_local_authentication: dependency: transitive description: - name: flutter_local_authentication - sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd - url: "https://pub.dev" - source: hosted + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index eca03a2a01..1d4064ac75 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -7,54 +7,50 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter - - # Ente packages + app_links: ^6.3.3 + bip39: ^1.0.6 + collection: ^1.18.0 + dio: ^5.4.0 + dotted_border: ^3.1.0 + email_validator: ^3.0.0 + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../events + ente_lock_screen: + path: ../lock_screen + ente_network: + path: ../network ente_strings: path: ../strings ente_ui: path: ../ui ente_utils: path: ../utils - ente_base: - path: ../base - ente_configuration: - path: ../configuration - ente_network: - path: ../network - ente_events: - path: ../events - ente_crypto_dart: - git: - url: https://github.com/ente-io/ente_crypto_dart.git - ente_lock_screen: - path: ../lock_screen - - # Third-party dependencies - bip39: ^1.0.6 - dio: ^5.4.0 - logging: ^1.2.0 - pointycastle: ^3.7.3 - shared_preferences: ^2.2.2 file_saver: ^0.3.0 - share_plus: ^11.0.0 - uuid: ^4.2.1 - collection: ^1.18.0 - dotted_border: ^3.1.0 + flutter: + sdk: flutter + logging: ^1.2.0 password_strength: ^0.2.0 + pinput: ^5.0.1 + pointycastle: ^3.7.3 + share_plus: ^11.0.0 + shared_preferences: ^2.2.2 step_progress_indicator: ^1.0.2 styled_text: ^8.1.0 - email_validator: ^3.0.0 - pinput: ^5.0.1 - app_links: ^6.3.3 url_launcher: ^6.3.1 url_launcher_ios: ^6.3.1 + uuid: ^4.2.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From be232efbc6d61961e5ba0e605972833413244333 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:17 +0530 Subject: [PATCH 057/164] Fix minor lint --- mobile/packages/accounts/lib/ente_accounts.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index 858677e3f9..9fcaf2bcf6 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,6 +1,3 @@ -/// A Flutter package containing account-related functionality for Ente apps -library ente_accounts; - export 'models/bonus.dart'; export 'models/delete_account.dart'; export 'models/sessions.dart'; From 7c33c160b22321d2295e9649b0eacf86330242cf Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:28 +0530 Subject: [PATCH 058/164] Lint base From b1dc9272a0e6e26d2f360dd75b81da7d2315faaf Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:15:23 +0530 Subject: [PATCH 059/164] Lint configuration From 2b3b7a5e2a4ece4b17f9407be3ea03eb511dcaa6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:16:24 +0530 Subject: [PATCH 060/164] Lint events From a5b0e66e9dc87928d70462777c6b501e9cf7c178 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:21:53 +0530 Subject: [PATCH 061/164] Lint lock_screen --- mobile/packages/lock_screen/lib/auth_util.dart | 2 -- .../lock_screen/lib/local_authentication_service.dart | 5 ----- mobile/packages/lock_screen/lib/ui/app_lock.dart | 1 - mobile/packages/lock_screen/lib/ui/lock_screen.dart | 3 --- .../packages/lock_screen/lib/ui/lock_screen_auto_lock.dart | 1 - .../lock_screen/lib/ui/lock_screen_confirm_password.dart | 1 - .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 2 -- mobile/packages/lock_screen/lib/ui/lock_screen_options.dart | 6 ------ .../packages/lock_screen/lib/ui/lock_screen_password.dart | 3 --- mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart | 4 ---- 10 files changed, 28 deletions(-) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index f529d507de..f4071b0712 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -8,8 +8,6 @@ import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index cb376120c9..eb1a65cf05 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -12,11 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; -import 'package:ente_lock_screen/ui/lock_screen_password.dart'; -import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index 2cf3daa915..9098737b7f 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:flutter/material.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index 3d0d4f0847..270d201eba 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -12,9 +12,6 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index c584193c47..16b5f45ffe 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -8,7 +8,6 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index 1fc07b8462..eb9e8d6824 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -6,7 +6,6 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index efa73b4b13..ecb4418968 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -6,8 +6,6 @@ import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index fb811f0e18..176172af95 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -20,12 +20,6 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; -import "package:ente_lock_screen/local_authentication_service.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/app_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index f3d857f494..eb5f2b94ed 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -11,9 +11,6 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 5eedad5ab6..603ebdd67d 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -12,10 +12,6 @@ import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/theme/text_style.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. From a375dfdc2ec00cb0d3b551ca24cd9233eb77ef82 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:21:53 +0530 Subject: [PATCH 062/164] Lint lock_screen From af36978ede8c6ffbf17a4d76469fec77a740d0ce Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:23:25 +0530 Subject: [PATCH 063/164] Lint logging --- mobile/packages/logging/analysis_options.yaml | 68 ++++++++++++++++++- mobile/packages/logging/lib/logging.dart | 5 +- .../logging/lib/{src => }/super_logging.dart | 2 +- .../lib/{src => }/tunneled_transport.dart | 0 mobile/packages/logging/pubspec.yaml | 2 +- 5 files changed, 70 insertions(+), 7 deletions(-) rename mobile/packages/logging/lib/{src => }/super_logging.dart (99%) rename mobile/packages/logging/lib/{src => }/tunneled_transport.dart (100%) diff --git a/mobile/packages/logging/analysis_options.yaml b/mobile/packages/logging/analysis_options.yaml index 609eb5d8aa..1bd78bc1b0 100644 --- a/mobile/packages/logging/analysis_options.yaml +++ b/mobile/packages/logging/analysis_options.yaml @@ -1,10 +1,72 @@ -include: package:flutter_lints/flutter.yaml +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks +include: package:flutter_lints/flutter.yaml linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/logging/lib/logging.dart b/mobile/packages/logging/lib/logging.dart index 9e31883c01..c3d827a240 100644 --- a/mobile/packages/logging/lib/logging.dart +++ b/mobile/packages/logging/lib/logging.dart @@ -1,3 +1,4 @@ -export 'src/super_logging.dart'; -export 'src/tunneled_transport.dart'; export 'package:logging/logging.dart'; + +export 'super_logging.dart'; +export 'tunneled_transport.dart'; diff --git a/mobile/packages/logging/lib/src/super_logging.dart b/mobile/packages/logging/lib/super_logging.dart similarity index 99% rename from mobile/packages/logging/lib/src/super_logging.dart rename to mobile/packages/logging/lib/super_logging.dart index f78b48d758..51af75d39a 100644 --- a/mobile/packages/logging/lib/src/super_logging.dart +++ b/mobile/packages/logging/lib/super_logging.dart @@ -3,7 +3,7 @@ import 'dart:collection'; import 'dart:core'; import 'dart:io'; -import 'tunneled_transport.dart'; +import 'package:ente_logging/tunneled_transport.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:http/http.dart' as http; diff --git a/mobile/packages/logging/lib/src/tunneled_transport.dart b/mobile/packages/logging/lib/tunneled_transport.dart similarity index 100% rename from mobile/packages/logging/lib/src/tunneled_transport.dart rename to mobile/packages/logging/lib/tunneled_transport.dart diff --git a/mobile/packages/logging/pubspec.yaml b/mobile/packages/logging/pubspec.yaml index 5dbf6ea650..aed0a718ed 100644 --- a/mobile/packages/logging/pubspec.yaml +++ b/mobile/packages/logging/pubspec.yaml @@ -21,8 +21,8 @@ dependencies: uuid: ^4.5.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 34bf4f6bbafd2d51c1faef1cdd6e87d94d86e7e5 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:25 +0530 Subject: [PATCH 064/164] Lint network --- mobile/packages/network/analysis_options.yaml | 72 +++++++++++++++++++ mobile/packages/network/lib/ente_network.dart | 2 - mobile/packages/network/pubspec.yaml | 6 +- 3 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 mobile/packages/network/analysis_options.yaml diff --git a/mobile/packages/network/analysis_options.yaml b/mobile/packages/network/analysis_options.yaml new file mode 100644 index 0000000000..1bd78bc1b0 --- /dev/null +++ b/mobile/packages/network/analysis_options.yaml @@ -0,0 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + +include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/network/lib/ente_network.dart b/mobile/packages/network/lib/ente_network.dart index d269ce93e5..418fd5eba1 100644 --- a/mobile/packages/network/lib/ente_network.dart +++ b/mobile/packages/network/lib/ente_network.dart @@ -1,3 +1 @@ -library ente_network; - export 'network.dart'; diff --git a/mobile/packages/network/pubspec.yaml b/mobile/packages/network/pubspec.yaml index bdcf9f9868..1e2e9b13d9 100644 --- a/mobile/packages/network/pubspec.yaml +++ b/mobile/packages/network/pubspec.yaml @@ -8,21 +8,21 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter dio: ^5.8.0+1 ente_configuration: path: ../../packages/configuration ente_events: path: ../../packages/events + flutter: + sdk: flutter native_dio_adapter: ^1.4.0 package_info_plus: ^8.3.0 ua_client_hints: ^1.4.1 uuid: ^4.5.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 487e4ef559fe635f916ab7aeda467f22b6d6ae25 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:34 +0530 Subject: [PATCH 065/164] Lint strings --- mobile/packages/strings/analysis_options.yaml | 72 ++++++++++++++++++- mobile/packages/strings/lib/ente_strings.dart | 5 +- mobile/packages/strings/lib/extensions.dart | 2 +- mobile/packages/strings/pubspec.yaml | 2 +- 4 files changed, 72 insertions(+), 9 deletions(-) diff --git a/mobile/packages/strings/analysis_options.yaml b/mobile/packages/strings/analysis_options.yaml index 4d72a29c19..1bd78bc1b0 100644 --- a/mobile/packages/strings/analysis_options.yaml +++ b/mobile/packages/strings/analysis_options.yaml @@ -1,6 +1,72 @@ -include: package:flutter_lints/flutter.yaml +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks -# Additional rules for Flutter packages +include: package:flutter_lints/flutter.yaml linter: rules: - public_member_api_docs: false + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/strings/lib/ente_strings.dart b/mobile/packages/strings/lib/ente_strings.dart index 9cb83e454f..171d58bdb5 100644 --- a/mobile/packages/strings/lib/ente_strings.dart +++ b/mobile/packages/strings/lib/ente_strings.dart @@ -1,5 +1,2 @@ -/// A Flutter package containing shared localization strings for Ente apps -library ente_strings; - -export 'l10n/strings_localizations.dart'; export 'extensions.dart'; +export 'l10n/strings_localizations.dart'; diff --git a/mobile/packages/strings/lib/extensions.dart b/mobile/packages/strings/lib/extensions.dart index 5aea9f908a..e60e135535 100644 --- a/mobile/packages/strings/lib/extensions.dart +++ b/mobile/packages/strings/lib/extensions.dart @@ -1,5 +1,5 @@ +import 'package:ente_strings/l10n/strings_localizations.dart'; import 'package:flutter/widgets.dart'; -import 'l10n/strings_localizations.dart'; // Re-export the localizations for convenience export 'l10n/strings_localizations.dart'; diff --git a/mobile/packages/strings/pubspec.yaml b/mobile/packages/strings/pubspec.yaml index e372063809..975215c2f8 100644 --- a/mobile/packages/strings/pubspec.yaml +++ b/mobile/packages/strings/pubspec.yaml @@ -14,9 +14,9 @@ dependencies: intl: any dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: generate: true From 465fc7c7d3fa827d99348ef77c37d3ae1e9f818a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:54 +0530 Subject: [PATCH 066/164] Lint ui --- mobile/packages/ui/analysis_options.yaml | 68 ++++++++++++++++++- .../lib/components/buttons/button_widget.dart | 2 +- .../lib/components/buttons/dynamic_fab.dart | 10 +-- .../lib/components/captioned_text_widget.dart | 2 +- .../ui/lib/components/divider_widget.dart | 2 +- .../ui/lib/components/loading_widget.dart | 2 +- .../components/title_bar_title_widget.dart | 2 +- .../ui/lib/components/title_bar_widget.dart | 4 +- .../ui/lib/pages/log_file_viewer.dart | 2 +- mobile/packages/ui/lib/pages/web_page.dart | 2 +- mobile/packages/ui/lib/theme/ente_theme.dart | 6 +- .../ui/lib/theme/ente_theme_data.dart | 4 +- .../packages/ui/lib/theme/multi_app_demo.dart | 9 +-- mobile/packages/ui/lib/theme/text_style.dart | 4 +- mobile/packages/ui/lib/utils/toast_util.dart | 2 +- mobile/packages/ui/pubspec.yaml | 6 +- 16 files changed, 96 insertions(+), 31 deletions(-) diff --git a/mobile/packages/ui/analysis_options.yaml b/mobile/packages/ui/analysis_options.yaml index 609eb5d8aa..1bd78bc1b0 100644 --- a/mobile/packages/ui/analysis_options.yaml +++ b/mobile/packages/ui/analysis_options.yaml @@ -1,10 +1,72 @@ -include: package:flutter_lints/flutter.yaml +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks +include: package:flutter_lints/flutter.yaml linter: rules: - - always_declare_return_types - - always_put_required_named_parameters_first + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/ui/lib/components/buttons/button_widget.dart b/mobile/packages/ui/lib/components/buttons/button_widget.dart index 29efd4c99c..ea4255981c 100644 --- a/mobile/packages/ui/lib/components/buttons/button_widget.dart +++ b/mobile/packages/ui/lib/components/buttons/button_widget.dart @@ -8,9 +8,9 @@ import 'package:ente_ui/theme/colors.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/theme/text_style.dart'; import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/debouncer.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; -import 'package:ente_utils/debouncer.dart'; enum ButtonSize { small, large } diff --git a/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart index 1ff4aab191..0cb31406dd 100644 --- a/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart +++ b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart @@ -64,10 +64,12 @@ class DynamicFAB extends StatelessWidget { child: OutlinedButton( onPressed: isFormValid! ? onPressedFunction as void Function()? : null, - child: Text(buttonText!, - style: isFormValid! - ? getEnteTextTheme(context).body - : getEnteTextTheme(context).bodyFaint), + child: Text( + buttonText!, + style: isFormValid! + ? getEnteTextTheme(context).body + : getEnteTextTheme(context).bodyFaint, + ), ), ); } diff --git a/mobile/packages/ui/lib/components/captioned_text_widget.dart b/mobile/packages/ui/lib/components/captioned_text_widget.dart index f4d6eb423a..bc9a9708d3 100644 --- a/mobile/packages/ui/lib/components/captioned_text_widget.dart +++ b/mobile/packages/ui/lib/components/captioned_text_widget.dart @@ -1,4 +1,4 @@ -import '../theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; class CaptionedTextWidget extends StatelessWidget { diff --git a/mobile/packages/ui/lib/components/divider_widget.dart b/mobile/packages/ui/lib/components/divider_widget.dart index c4a3374abb..71d8165cf0 100644 --- a/mobile/packages/ui/lib/components/divider_widget.dart +++ b/mobile/packages/ui/lib/components/divider_widget.dart @@ -1,4 +1,4 @@ -import '../theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; enum DividerType { diff --git a/mobile/packages/ui/lib/components/loading_widget.dart b/mobile/packages/ui/lib/components/loading_widget.dart index 999f0aa3c2..113d9e199f 100644 --- a/mobile/packages/ui/lib/components/loading_widget.dart +++ b/mobile/packages/ui/lib/components/loading_widget.dart @@ -1,4 +1,4 @@ -import '../theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; class EnteLoadingWidget extends StatelessWidget { diff --git a/mobile/packages/ui/lib/components/title_bar_title_widget.dart b/mobile/packages/ui/lib/components/title_bar_title_widget.dart index 3af955fea3..184c5726a1 100644 --- a/mobile/packages/ui/lib/components/title_bar_title_widget.dart +++ b/mobile/packages/ui/lib/components/title_bar_title_widget.dart @@ -1,4 +1,4 @@ -import '../theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; class TitleBarTitleWidget extends StatelessWidget { diff --git a/mobile/packages/ui/lib/components/title_bar_widget.dart b/mobile/packages/ui/lib/components/title_bar_widget.dart index 9517bc1749..ab60e0531c 100644 --- a/mobile/packages/ui/lib/components/title_bar_widget.dart +++ b/mobile/packages/ui/lib/components/title_bar_widget.dart @@ -1,5 +1,5 @@ -import '../theme/ente_theme.dart'; -import '../components/buttons/icon_button_widget.dart'; +import 'package:ente_ui/components/buttons/icon_button_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; class TitleBarWidget extends StatelessWidget { diff --git a/mobile/packages/ui/lib/pages/log_file_viewer.dart b/mobile/packages/ui/lib/pages/log_file_viewer.dart index d002556852..bfbfeda15a 100644 --- a/mobile/packages/ui/lib/pages/log_file_viewer.dart +++ b/mobile/packages/ui/lib/pages/log_file_viewer.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'package:ente_ui/components/loading_widget.dart'; -import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; class LogFileViewer extends StatefulWidget { final File file; diff --git a/mobile/packages/ui/lib/pages/web_page.dart b/mobile/packages/ui/lib/pages/web_page.dart index 10652d0e51..e93f8b153e 100644 --- a/mobile/packages/ui/lib/pages/web_page.dart +++ b/mobile/packages/ui/lib/pages/web_page.dart @@ -1,4 +1,4 @@ -import '../components/loading_widget.dart'; +import 'package:ente_ui/components/loading_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; diff --git a/mobile/packages/ui/lib/theme/ente_theme.dart b/mobile/packages/ui/lib/theme/ente_theme.dart index 25e16e705f..b8825bb70b 100644 --- a/mobile/packages/ui/lib/theme/ente_theme.dart +++ b/mobile/packages/ui/lib/theme/ente_theme.dart @@ -1,7 +1,7 @@ +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/effects.dart'; +import 'package:ente_ui/theme/text_style.dart'; import 'package:flutter/material.dart'; -import 'colors.dart'; -import 'effects.dart'; -import 'text_style.dart'; class EnteTheme { final EnteTextTheme textTheme; diff --git a/mobile/packages/ui/lib/theme/ente_theme_data.dart b/mobile/packages/ui/lib/theme/ente_theme_data.dart index 8e2448feb1..8c2cf18786 100644 --- a/mobile/packages/ui/lib/theme/ente_theme_data.dart +++ b/mobile/packages/ui/lib/theme/ente_theme_data.dart @@ -1,6 +1,6 @@ -import '../theme/colors.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import '../theme/ente_theme.dart'; final lightThemeData = ThemeData( fontFamily: 'Inter', diff --git a/mobile/packages/ui/lib/theme/multi_app_demo.dart b/mobile/packages/ui/lib/theme/multi_app_demo.dart index 939cbe81bb..13f8cd9f70 100644 --- a/mobile/packages/ui/lib/theme/multi_app_demo.dart +++ b/mobile/packages/ui/lib/theme/multi_app_demo.dart @@ -1,10 +1,10 @@ // Demo: Complete working example showing multi-app theme compatibility // This file demonstrates how the reusable theme system works for different apps +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme_data.dart'; import 'package:flutter/material.dart'; -import 'colors.dart'; -import 'ente_theme_data.dart'; -import 'ente_theme.dart'; /// App 1: E-commerce app with blue theme class ECommerceApp { @@ -210,7 +210,8 @@ class DemoHomePage extends StatelessWidget { final isSelected = index == currentAppIndex; return FilterChip( label: Text( - ['E-commerce', 'Social', 'Finance', 'Gaming'][index]), + ['E-commerce', 'Social', 'Finance', 'Gaming'][index], + ), selected: isSelected, onSelected: (_) => onAppChanged(index), backgroundColor: colorScheme.fillFaint, diff --git a/mobile/packages/ui/lib/theme/text_style.dart b/mobile/packages/ui/lib/theme/text_style.dart index d6b69ca83f..f8b8caad7b 100644 --- a/mobile/packages/ui/lib/theme/text_style.dart +++ b/mobile/packages/ui/lib/theme/text_style.dart @@ -1,6 +1,6 @@ +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/platform_text_config.dart'; import 'package:flutter/material.dart'; -import '../theme/colors.dart'; -import '../theme/platform_text_config.dart'; const FontWeight _regularWeight = FontWeight.w500; const FontWeight _boldWeight = FontWeight.w600; diff --git a/mobile/packages/ui/lib/utils/toast_util.dart b/mobile/packages/ui/lib/utils/toast_util.dart index 8b03a1f778..336956232e 100644 --- a/mobile/packages/ui/lib/utils/toast_util.dart +++ b/mobile/packages/ui/lib/utils/toast_util.dart @@ -21,7 +21,7 @@ void showToast( fontSize: 16.0, ); } on MissingPluginException catch (_) { - Widget toast = Container( + final toast = Container( padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), decoration: BoxDecoration( borderRadius: BorderRadius.circular(25.0), diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml index e501e11fad..15be90cfc2 100644 --- a/mobile/packages/ui/pubspec.yaml +++ b/mobile/packages/ui/pubspec.yaml @@ -8,8 +8,6 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter dio: ^5.8.0+1 ente_base: path: ../../packages/base @@ -21,6 +19,8 @@ dependencies: path: ../../packages/strings ente_utils: path: ../../packages/utils + flutter: + sdk: flutter flutter_inappwebview: ^6.1.5 fluttertoast: ^8.1.1 modal_bottom_sheet: ^3.0.0 @@ -28,8 +28,8 @@ dependencies: window_manager: ^0.5.0 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From b61c75dc84d324bffbcca5fef611c29ca60979f4 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:58 +0530 Subject: [PATCH 067/164] Lint utils --- mobile/packages/utils/analysis_options.yaml | 72 +++++++++++++++++++ .../packages/utils/lib/directory_utils.dart | 2 +- mobile/packages/utils/lib/email_util.dart | 4 +- mobile/packages/utils/lib/ente_utils.dart | 4 +- mobile/packages/utils/pubspec.yaml | 16 ++--- 5 files changed, 83 insertions(+), 15 deletions(-) create mode 100644 mobile/packages/utils/analysis_options.yaml diff --git a/mobile/packages/utils/analysis_options.yaml b/mobile/packages/utils/analysis_options.yaml new file mode 100644 index 0000000000..1bd78bc1b0 --- /dev/null +++ b/mobile/packages/utils/analysis_options.yaml @@ -0,0 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + +include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/utils/lib/directory_utils.dart b/mobile/packages/utils/lib/directory_utils.dart index 7fdbc6de67..c3b36dba08 100644 --- a/mobile/packages/utils/lib/directory_utils.dart +++ b/mobile/packages/utils/lib/directory_utils.dart @@ -21,7 +21,7 @@ class DirectoryUtils { } static Future getDirectoryForInit() async { - Directory directory = await getApplicationCacheDirectory(); + final directory = await getApplicationCacheDirectory(); return Directory(p.join(directory.path, "init")); } diff --git a/mobile/packages/utils/lib/email_util.dart b/mobile/packages/utils/lib/email_util.dart index 21ccada2f8..ea1c5220ae 100644 --- a/mobile/packages/utils/lib/email_util.dart +++ b/mobile/packages/utils/lib/email_util.dart @@ -2,20 +2,20 @@ import 'dart:io'; import 'package:archive/archive_io.dart'; import 'package:email_validator/email_validator.dart'; +import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_logging/logging.dart'; import 'package:ente_strings/extensions.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/components/dialog_widget.dart'; import 'package:ente_ui/pages/log_file_viewer.dart'; -import 'package:flutter_email_sender/flutter_email_sender.dart'; -import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_utils/directory_utils.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; import "package:file_saver/file_saver.dart"; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_email_sender/flutter_email_sender.dart'; import "package:intl/intl.dart"; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/mobile/packages/utils/lib/ente_utils.dart b/mobile/packages/utils/lib/ente_utils.dart index 1f18b2af82..d9e96ad0c7 100644 --- a/mobile/packages/utils/lib/ente_utils.dart +++ b/mobile/packages/utils/lib/ente_utils.dart @@ -1,9 +1,7 @@ -library ente_utils; - export 'debouncer.dart'; export 'directory_utils.dart'; export 'email_util.dart'; export 'fake_progress.dart'; +export 'navigation_util.dart'; export 'platform_util.dart'; export 'share_utils.dart'; -export 'navigation_util.dart'; diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 3a6a77e4ea..4ec3dd3c90 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -8,19 +8,20 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter - # Core dependencies archive: ^4.0.7 email_validator: ^3.0.0 - file_saver: ^0.3.0 - flutter_email_sender: ^7.0.0 + ente_configuration: + path: ../../packages/configuration ente_logging: path: ../../packages/logging ente_strings: path: ../../packages/strings ente_ui: path: ../../packages/ui + file_saver: ^0.3.0 + flutter: + sdk: flutter + flutter_email_sender: ^7.0.0 intl: ^0.20.1 logging: ^1.3.0 package_info_plus: ^8.1.1 @@ -30,13 +31,10 @@ dependencies: url_launcher: ^6.3.1 window_manager: ^0.5.0 - # Ente packages - ente_configuration: - path: ../../packages/configuration dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 49106a3dd90b5813920211409fb60e2729efcb3e Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:55:53 +0530 Subject: [PATCH 068/164] Pull existing translations --- .../strings/lib/l10n/arb/strings_ar.arb | 707 +++++++++++++++++- .../strings/lib/l10n/arb/strings_be.arb | 331 ++++++++ .../strings/lib/l10n/arb/strings_bg.arb | 701 ++++++++++++++++- .../strings/lib/l10n/arb/strings_ca.arb | 700 +++++++++++++++++ .../strings/lib/l10n/arb/strings_cs.arb | 697 ++++++++++++++++- .../strings/lib/l10n/arb/strings_da.arb | 689 ++++++++++++++++- .../strings/lib/l10n/arb/strings_de.arb | 704 +++++++++++++++++ .../strings/lib/l10n/arb/strings_el.arb | 693 ++++++++++++++++- .../strings/lib/l10n/arb/strings_en.arb | 17 +- .../strings/lib/l10n/arb/strings_es.arb | 697 ++++++++++++++++- .../strings/lib/l10n/arb/strings_et.arb | 306 ++++++++ .../strings/lib/l10n/arb/strings_fa.arb | 620 +++++++++++++++ .../strings/lib/l10n/arb/strings_fi.arb | 271 +++++++ .../strings/lib/l10n/arb/strings_fr.arb | 699 ++++++++++++++++- .../strings/lib/l10n/arb/strings_gu.arb | 90 +++ .../strings/lib/l10n/arb/strings_he.arb | 478 ++++++++++++ .../strings/lib/l10n/arb/strings_hi.arb | 102 +++ .../strings/lib/l10n/arb/strings_hu.arb | 704 +++++++++++++++++ .../strings/lib/l10n/arb/strings_id.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_it.arb | 704 +++++++++++++++++ .../strings/lib/l10n/arb/strings_ja.arb | 699 ++++++++++++++++- .../strings/lib/l10n/arb/strings_ka.arb | 110 +++ .../strings/lib/l10n/arb/strings_km.arb | 188 +++++ .../strings/lib/l10n/arb/strings_ko.arb | 701 ++++++++++++++++- .../strings/lib/l10n/arb/strings_lt.arb | 701 ++++++++++++++++- .../strings/lib/l10n/arb/strings_lv.arb | 125 ++++ .../strings/lib/l10n/arb/strings_ml.arb | 62 ++ .../strings/lib/l10n/arb/strings_nl.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_pl.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_pt.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_ro.arb | 311 ++++++++ .../strings/lib/l10n/arb/strings_ru.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_sk.arb | 693 ++++++++++++++++- .../strings/lib/l10n/arb/strings_sl.arb | 700 +++++++++++++++++ .../strings/lib/l10n/arb/strings_sr.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_sv.arb | 697 ++++++++++++++++- .../strings/lib/l10n/arb/strings_ti.arb | 542 ++++++++++++++ .../strings/lib/l10n/arb/strings_tr.arb | 705 ++++++++++++++++- .../strings/lib/l10n/arb/strings_uk.arb | 692 +++++++++++++++++ .../strings/lib/l10n/arb/strings_vi.arb | 701 ++++++++++++++++- .../strings/lib/l10n/arb/strings_zh.arb | 701 ++++++++++++++++- .../strings/lib/l10n/arb/strings_zh_CN.arb | 704 +++++++++++++++++ .../strings/lib/l10n/arb/strings_zh_TW.arb | 705 ++++++++++++++++- 43 files changed, 23810 insertions(+), 67 deletions(-) create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_be.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ca.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_de.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_et.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_fa.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_fi.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_gu.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_he.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_hi.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_hu.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_it.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ka.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_km.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_lv.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ml.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ro.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_sl.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_ti.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_uk.arb create mode 100644 mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ar.arb b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb index a5580b6ce3..89b0360355 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ar.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb @@ -1,9 +1,704 @@ { "networkHostLookUpErr": "تعذر الاتصال بـEnte، فضلا تحقق من إعدادات الشبكة الخاصة بك وتواصل مع الدعم إذا استمر الخطأ.", - "networkConnectionRefusedErr": "غير قادر على الاتصال بـ Ente، يرجى إعادة المحاولة بعد فترة. إذا استمر الخطأ، يرجى الاتصال بالدعم.", - "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "يبدو أن خطأ ما حدث. يرجى إعادة المحاولة بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "تعذر الإتصال بـEnte، فضلا أعد المحاولة لاحقا. إذا استمر الخطأ، فضلا تواصل مع الدعم.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "يبدو أنه حدث خطأ ما. الرجاء إعادة المحاولة لاحقا. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, "error": "خطأ", - "ok": "موافق", - "faq": "الأسئلة الشائعة", - "contactSupport": "اتصل بالدعم" -} + "@error": { + "description": "Generic error title" + }, + "ok": "حسناً", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "الأسئلة الأكثر شيوعاً", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "الاتصال بالدعم", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "إرسال السجلات عبر البريد الإلكتروني", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "الرجاء إرسال السجلات إلى {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "نسخ عنوان البريد الإلكتروني", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "تصدير السجلات", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "إلغاء", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "ألإبلاغ عن خلل تقني", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "متصل بـ{endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "حفظ", + "@save": { + "description": "Label for save button" + }, + "send": "إرسال", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا) أم إرساله إلى تطبيقات أخرى؟", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا)؟", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "أدخل عنوان بريدك الإلكتروني الجديد", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "البريد الإلكتروني", + "@email": { + "description": "Email field label" + }, + "verify": "التحقق", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "عنوان البريد الإلكتروني غير صالح", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "الرجاء إدخال بريد إلكتروني صالح.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "انتظر قليلاً...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "التحقق من كلمة المرور", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "كلمة المرور غير صحيحة", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "يرجى المحاولة مرة أخرى", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "أدخل كلمة المرور", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "أدخل كلمة المرور الخاصة بك", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "الجلسات النشطة", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "عذرًا", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "حدث خطأ ما، يرجى المحاولة مرة أخرى", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "إنهاء الجلسة؟", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "إنهاء", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "هذا الجهاز", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "إنشاء حساب", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "ضعيف", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "متوسط", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "قوي", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "إزالة الحساب", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "سوف نأسف لرؤيتك تذهب. هل تواجه بعض المشاكل؟", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "نعم، ارسل الملاحظات", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "لا، حذف الحساب", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "الرجاء المصادقة لبدء حذف الحساب", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "تأكيد حذف الحساب", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "هذا الحساب مرتبط بتطبيقات Ente أخرى، إذا كنت تستخدم أحدها.\n\nسنضع موعدا لحذف بياناتك المرفوعة عبر كل تطبيقات Ente، وسيتم حذف حسابك بصورة دائمة.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "حذف", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "إنشاء حساب جديد", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "كلمة المرور", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "تأكيد كلمة المرور", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "قوة كلمة المرور: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "كيف سمعت عن Ente؟ (اختياري)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "نحن لا نتتبع تثبيت التطبيق. سيكون من المفيد إذا أخبرتنا أين وجدتنا!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "أوافق على شروط الخدمة وسياسة الخصوصية", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "الشروط", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "سياسة الخصوصية", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "أنا أفهم أنه إذا فقدت كلمة المرور الخاصة بي، قد أفقد بياناتي لأن بياناتي هي مشفرة من الند للند.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "التشفير", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "تسجيل الدخول", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "مرحبًا مجددًا!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "بالنقر على تسجيل الدخول، أوافق على شروط الخدمة و سياسة الخصوصية", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "لا يوجد اتصال بالإنترنت", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "يرجى التحقق من اتصالك بالإنترنت ثم المحاولة من جديد.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "فشل في المصادقة ، يرجى المحاولة مرة أخرى في وقت لاحق", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "إعادة كتابة كلمة المرور", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "استخدم مفتاح الاسترداد", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "هل نسيت كلمة المرور", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "غير البريد الإلكتروني", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "تأكيد البريد الإلكتروني", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "لقد أرسلنا رسالة إلى {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "لإعادة تعيين كلمة المرور الخاصة بك، يرجى التحقق من بريدك الإلكتروني أولاً.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "الرجاء التحقق من صندوق الوارد (والرسائل غير المرغوب فيها) لإكمال التحقق", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "انقر لإدخال الرمز", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "إرسال بريد إلكتروني", + "resendEmail": "إعادة إرسال البريد الإلكتروني", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "التحقق ما زال جارٍ", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "انتهت صلاحية الجلسة", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "انتهت صلاحية جلستك. فضلا أعد تسجيل الدخول.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "التحقق من مفتاح المرور", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "بانتظار التحقق...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "حاول مرة أخرى", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "تحقق من الحالة", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "إسترجاع الحساب", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "تعيين كلمة المرور", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "تغيير كلمة المرور", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "إعادة تعيين كلمة المرور", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "مفاتيح التشفير", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "أدخل كلمة المرور التي يمكننا استخدامها لتشفير بياناتك", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "أدخل كلمة مرور جديدة يمكننا استخدامها لتشفير بياناتك", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، لا يمكننا فك تشفير بياناتك", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "كيف يعمل", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "توليد مفاتيح التشفير...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "تم تغيير كلمة المرور بنجاح", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "تسجيل الخروج من الأجهزة الأخرى", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "إذا كنت تعتقد أن شخصا ما يعرف كلمة المرور الخاصة بك، يمكنك إجبار جميع الأجهزة الأخرى الستخدمة حاليا لحسابك على تسجيل الخروج.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "تسجيل الخروج من الأجهزة الأخرى", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "لا تقم بتسجيل الخروج", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "توليد مفاتيح التشفير...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "المتابعة", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "جهاز غير آمن", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "عذرًا، لم نتمكن من إنشاء مفاتيح آمنة على هذا الجهاز.\n\nيرجى التسجيل من جهاز مختلف.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "تم نسخ عبارة الاسترداد للحافظة", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "مفتاح الاسترداد", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد بياناتك هي بهذا المفتاح.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "نحن لا نخزن هذا المفتاح، يرجى حفظ مفتاح الـ 24 كلمة هذا في مكان آمن.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "قم بهذا لاحقاً", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "حفظ المفتاح", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "حُفِظ مفتاح الاستعادة في مجلد التنزيلات!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "لا يوجد مفتاح استرجاع؟", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "المصادقة الثنائية", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "أدخل الرمز المكون من 6 أرقام من\nتطبيق المصادقة", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "جهاز مفقود ؟", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "أدخل رمز الاسترداد", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "استرداد", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "جاري تسجيل الخروج...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "فورًا", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "قُفْل التطبيق", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "قفل تلقائي", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "لا يوجد قفل نظام", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "لتفعيل قُفْل الجهاز، اضبط رمز مرور أو قُفْل الشاشة من الإعدادات", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "اختر نوع قُفْل الشاشة: افتراضي أو مخصص.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "قفل الجهاز", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "قفل رقم التعريف الشخصي", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "الوقت الذي بعده ينقفل التطبيق بعدما يوضع في الخلفية", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "أخفِ المحتوى", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "يخفي محتوى التطبيق في مبدل التطبيقات ويمنع لقطات الشاشة", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "يخفي محتوى التطبيق في مبدل التطبيقات", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "محاولات خاطئة أكثر من المسموح", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "المس لإلغاء القفل", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "هل أنت متأكد من أنك تريد تسجيل الخروج؟", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "نعم، تسجيل الخروج", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "التالي", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "عين كلمة مرور جديدة", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "أدخل رقم التعريف الشخصي", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "عين رقم تعريف شخصي جديد", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "تأكيد", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "أعد إدخال كلمة المرور", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "أعد إدخال رقم التعريف الشخصي", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "التحقق من الهوية", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "لم يتم التعرف عليه. حاول مرة أخرى.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "تم بنجاح", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "إلغاء", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "المصادقة مطلوبة", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "البيومترية مطلوبة", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "بيانات اعتماد الجهاز مطلوبة", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "بيانات اعتماد الجهاز مطلوبة", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "الانتقال إلى الإعدادات", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "لم يتم إعداد المصادقة الحيوية على جهازك. انتقل إلى 'الإعدادات > الأمن' لإضافة المصادقة البيومترية.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "المصادقة البيومترية معطلة. الرجاء قفل الشاشة وفتح القفل لتفعيلها.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "حسناً", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "البريد الإلكتروني مُسَجَّل من قبل.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "البريد الإلكتروني غير مُسَجَّل.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "هذا البريد مستخدم مسبقاً", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "تم تغيير البريد الإلكتروني إلى {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "فشلت المصادقة. الرجاء المحاولة مرة أخرى", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "تمت المصادقة بنجاح!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "انتهت صَلاحِيَة الجِلسة", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "مفتاح الاسترداد غير صحيح", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "مفتاح الاسترداد الذي أدخلته غير صحيح", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "تم تحديث المصادقة الثنائية بنجاح", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "انتهت صلاحية رمز التحقق", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "رمز غير صحيح", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "عذراً، الرمز الذي أدخلته غير صحيح", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_be.arb b/mobile/packages/strings/lib/l10n/arb/strings_be.arb new file mode 100644 index 0000000000..b8b9d319d9 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_be.arb @@ -0,0 +1,331 @@ +{ + "error": "Памылка", + "@error": { + "description": "Generic error title" + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Частыя пытанні", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Звярнуцца ў службу падтрымкі", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Адправіць журналы", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "exportLogs": "Экспартаваць журналы", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Скасаваць", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Паведаміць аб памылцы", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "save": "Захаваць", + "@save": { + "description": "Label for save button" + }, + "send": "Адправіць", + "@send": { + "description": "Label for send button" + }, + "email": "Электронная пошта", + "@email": { + "description": "Email field label" + }, + "verify": "Праверыць", + "@verify": { + "description": "Verify button label" + }, + "pleaseWait": "Пачакайце...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Праверыць пароль", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Няправільны пароль", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Калі ласка, паспрабуйце яшчэ раз", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Увядзіце пароль", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Увядзіце ваш пароль", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Актыўныя сеансы", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Вой", + "@oops": { + "description": "Oops error title" + }, + "terminate": "Перарваць", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Гэта прылада", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Стварыць уліковы запіс", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Ненадзейны", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Умераная", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Надзейны", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Выдаліць уліковы запіс", + "@deleteAccount": { + "description": "Delete account button label" + }, + "noDeleteAccountAction": "Не, выдаліць уліковы запіс", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "delete": "Выдаліць", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Стварыць новы ўліковы запіс", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Пароль", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Пацвердзіць пароль", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "termsOfServicesTitle": "Умовы", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Палітыка прыватнасці", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "encryption": "Шыфраванне", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Увайсці", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "З вяртаннем!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "useRecoveryKey": "Выкарыстоўваць ключ аднаўлення", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Забылі пароль", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Змяніць адрас электроннай пошты", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Праверыць электронную пошту", + "@verifyEmail": { + "description": "Verify email title" + }, + "sendEmail": "Адправіць ліст", + "resendEmail": "Адправіць ліст яшчэ раз", + "@resendEmail": { + "description": "Resend email button label" + }, + "loginSessionExpired": "Сеанс завяршыўся", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "tryAgain": "Паспрабуйце яшчэ раз", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Праверыць статус", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Увайсці з TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Аднавіць уліковы запіс", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Задаць пароль", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Змяніць пароль", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Скінуць пароль", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Ключы шыфравання", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "howItWorks": "Як гэта працуе", + "@howItWorks": { + "description": "How it works button label" + }, + "doNotSignOut": "Не выходзіць", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Генерацыя ключоў шыфравання...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Працягнуць", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Небяспечная прылада", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "recoveryKey": "Ключ аднаўлення", + "@recoveryKey": { + "description": "Recovery key label" + }, + "doThisLater": "Зрабіць гэта пазней", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Захаваць ключ", + "@saveKey": { + "description": "Save key button label" + }, + "lostDeviceTitle": "Згубілі прыладу?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "recover": "Аднавіць", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Выхад...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Адразу", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Блакіроўка праграмы", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Аўтаблакіроўка", + "@autoLock": { + "description": "Auto lock setting title" + }, + "deviceLock": "Блакіроўка прылады", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Блакіроўка PIN'ам", + "@pinLock": { + "description": "PIN lock option title" + }, + "hideContent": "Схаваць змест", + "@hideContent": { + "description": "Hide content setting title" + }, + "tapToUnlock": "Націсніце для разблакіроўкі", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "yesLogout": "Так, выйсці", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "next": "Далей", + "@next": { + "description": "Next button label" + }, + "enterPin": "Увядзіце PIN-код", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Задаць новы PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Пацвердзіць", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPin": "Увядзіце PIN-код яшчэ раз", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Праверыць ідэнтыфікацыю", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Паспяхова", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Скасаваць", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "goToSettings": "Перайсці ў налады", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "sessionExpired": "Сеанс завяршыўся", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectCode": "Няправільны код", + "@incorrectCode": { + "description": "Error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_bg.arb b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb index 25b6e48826..78ccb634e6 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_bg.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb @@ -1,3 +1,700 @@ { - "networkHostLookUpErr": "Не може да се свърже с Ente, моля, проверете мрежовите си настройки и се свържете с поддръжката, ако проблемът продължава." -} + "networkHostLookUpErr": "Не може да се свърже с Ente, моля, проверете мрежовите си настройки и се свържете с поддръжката, ако проблемът продължава.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Не може да се свърже с Ente, моля, опитайте отново след известно време. Ако проблемът продължава, моля, свържете се с поддръжката.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Изглежда нещо се обърка. Моля, опитайте отново след известно време. Ако грешката продължава, моля, свържете се с нашия екип за поддръжка.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Грешка", + "@error": { + "description": "Generic error title" + }, + "ok": "Ок", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "ЧЗВ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Свържете се с поддръжката", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Изпратете Вашата история на действията на имейл", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Моля, изпратете историята на действията на \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Копиране на имейл адрес", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Експорт на файловете с историята", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Отказ", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Докладване на проблем", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Свързан към {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Запазване", + "@save": { + "description": "Label for save button" + }, + "send": "Изпращане", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране) или да го изпратите на други приложения?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Имейл", + "@email": { + "description": "Email field label" + }, + "verify": "Потвърждаване", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Невалиден имейл адрес", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Моля, въведете валиден имейл адрес.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Моля изчакайте...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Потвърдете паролата", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Грешна парола", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Опитайте отново", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Въведете парола", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Въведете паролата си", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Активни сесии", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Опа", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Нещо се обърка, моля опитайте отново", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Това ще Ви изкара от профила на това устройство!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Това ще Ви изкара от профила на следното устройство:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Прекратяване на сесията?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Прекратяване", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Това устройство", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Създаване на акаунт", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Слаба", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Умерена", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Силна", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Изтриване на акаунта", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Ще съжаляваме да си тръгнете. Изправени ли сте пред някакъв проблем?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Да, изпращане на обратна връзка", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Не, изтриване на акаунта", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Моля, удостоверете се, за да инициирате изтриването на акаунта", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Потвърдете изтриването на акаунта", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Този акаунт е свързан с други приложения на Ente, ако използвате такива.\n\nВашите качени данни във всички приложения на Ente ще бъдат планирани за изтриване и акаунтът Ви ще бъде изтрит за постоянно.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Изтриване", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Създаване на нов акаунт", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Парола", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Потвърждаване на паролата", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Сила на паролата: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Как научихте за Ente? (по избор)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Ние не проследяваме инсталиранията на приложения. Ще помогне, ако ни кажете къде ни намерихте!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Съгласявам се с условията за ползване и политиката за поверителност", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Условия", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Политика за поверителност", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Разбирам, че ако загубя паролата си, може да загубя данните си, тъй като данните ми са шифровани от край до край.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Шифроване", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Вход", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Добре дошли отново!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "С натискането на вход, се съгласявам с условията за ползване и политиката за поверителност", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Няма връзка с интернет", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Моля, проверете интернет връзката си и опитайте отново.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Неуспешно проверка, моля опитайте отново", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Създайте отново парола", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, но можем да я регенерираме по начин, който работи с всички устройства.\n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Използвайте ключ за възстановяване", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Забравена парола", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Промяна на имейл", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Потвърдете имейла", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Изпратихме имейл до {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "За да нулирате паролата си, моля, първо потвърдете своя имейл.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Моля, проверете входящата си поща (и спама), за да завършите проверката", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Докоснете, за да въведете код", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Изпратете имейл", + "resendEmail": "Повторно изпращане на имейл", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Потвърждението все още се изчаква", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Сесията изтече", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Вашата сесия изтече. Моля влезте отново.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Удостоверяване с ключ за парола", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Изчаква се потвърждение...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Опитайте отново", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Проверка на състоянието", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Влизане с еднократен код", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Възстановяване на акаунт", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Задаване на парола", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Промяна на паролата", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Нулиране на паролата", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Ключове за шифроване", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Въведете парола, която да използваме за шифроване на Вашите данни", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Въведете нова парола, която да използваме за шифроване на Вашите данни", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Ние не съхраняваме тази парола, така че ако я забравите, не можем да дешифрираме Вашите данни", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Как работи", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Генериране на ключове за шифроване...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Паролата е променена успешно", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Излизане от други устройства", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Ако смятате, че някой може да знае паролата Ви, можете да принудите всички други устройства, използващи Вашия акаунт, да излязат.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Излизане от други устройства", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Не излизайте", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Генерират се ключове за шифроване...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Продължете", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Несигурно устройство", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "За съжаление не можахме да генерираме защитени ключове на това устройство.\n\nМоля, регистрирайте се от друго устройство.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Ключът за възстановяване е копиран в буферната памет", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Ключ за възстановяване", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Ако забравите паролата си, единственият начин да възстановите данните си е с този ключ.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Ние не съхраняваме този ключ, моля, запазете този ключ от 24 думи на сигурно място.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Направете това по-късно", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Запазване на ключа", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Ключът за възстановяване е запазен в папка за Изтегляния!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Няма ключ за възстановяване?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Двуфакторно удостоверяване", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Въведете 6-цифрения код от\nВашето приложение за удостоверяване", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Загубено устройство?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Въведете Вашия ключ за възстановяване", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Възстановяване", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Излизане от профила...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Незабавно", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Заключване на приложението", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Автоматично заключване", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Не е намерено заключване на системата", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "За да активирате заключването на устройството, моля, задайте парола за устройството или заключване на екрана в системните настройки.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Изберете между заключен екран по подразбиране на Вашето устройство и персонализиран заключен екран с ПИН код или парола.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Заключване на устройството", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Заключване с ПИН код", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Време, след което приложението се заключва, след като е поставено на заден план", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Скриване на съдържанието", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Скрива съдържанието на приложението в превключвателя на приложения и деактивира екранните снимки", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Скрива съдържанието на приложението в превключвателя на приложения", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Твърде много неуспешни опити", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Докоснете, за да отключите", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Наистина ли искате да излезете от профила си?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Да, излез", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Моля, удостоверете се, за да видите Вашите кодове", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Следващ", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Задаване на нова парола", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Въведете ПИН код", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Задаване на нов ПИН код", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Потвърждаване", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Въведете отново паролата", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Въведете отново ПИН кода", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Потвърждаване на самоличността", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Не е разпознат. Опитайте отново.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Успешно", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Отказ", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Необходимо е удостоверяване", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Изискват се биометрични данни", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Изискват се идентификационни данни за устройството", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Изискват се идентификационни данни за устройството", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Отваряне на настройките", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Биометричното удостоверяване не е настроено на Вашето устройство. Отидете на „Настройки > Сигурност“, за да добавите биометрично удостоверяване.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Биометричното удостоверяване е деактивирано. Моля, заключете и отключете екрана си, за да го активирате.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "ОК", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Имейлът вече е регистриран.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Имейлът не е регистриран.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Този имейл вече се използва", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Имейлът е променен на {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Неуспешно удостоверяване, моля опитайте отново", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Успешно удостоверяване!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Сесията е изтекла", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Неправилен ключ за възстановяване", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Въведеният от Вас ключ за възстановяване е неправилен", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Двуфакторното удостоверяване бе успешно нулирано", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Вашият код за потвърждение е изтекъл", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Неправилен код", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "За съжаление кодът, който сте въвели, е неправилен", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ca.arb b/mobile/packages/strings/lib/l10n/arb/strings_ca.arb new file mode 100644 index 0000000000..b8c332d65f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ca.arb @@ -0,0 +1,700 @@ +{ + "networkHostLookUpErr": "No s'ha pogut connectar a Ente, si us plau, comprova la configuració de la xarxa i contacta amb suport si l'error persisteix.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "No s'ha pogut connectar a Ente, si us plau, torna-ho a intentar després d'un temps. Si l'error persisteix, contacta amb suport.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Sembla que alguna cosa ha anat malament. Si us plau, torna-ho a intentar després d'un temps. Si l'error persisteix, contacta amb el nostre equip de suport.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Error", + "@error": { + "description": "Generic error title" + }, + "ok": "D'acord", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contacta amb suport", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Envia els teus registres per correu", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Si us plau, envia els registres a \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copia l'adreça de correu", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exporta els registres", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Cancel·la", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Informa d'un error", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Connectat a {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Guarda", + "@save": { + "description": "Label for save button" + }, + "send": "Envia", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Vols guardar-ho al teu emmagatzematge (per defecte, a la carpeta Descàrregues) o enviar-ho a altres aplicacions?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Vols guardar-ho al teu emmagatzematge (per defecte, a la carpeta Descàrregues)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Correu electrònic", + "@email": { + "description": "Email field label" + }, + "verify": "Verifica", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Adreça de correu electrònic no vàlida", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Si us plau, introdueix una adreça de correu electrònic vàlida.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Si us plau, espera...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verifica la contrasenya", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Contrasenya incorrecta", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Si us plau, intenta-ho de nou", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Introdueix la contrasenya", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Introdueix la teva contrasenya", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sessions actives", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "S'ha produït un error, si us plau, intenta-ho de nou", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Això tancarà la sessió en aquest dispositiu!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Això tancarà la sessió en el següent dispositiu:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Finalitzar sessió?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Finalitzar", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Aquest dispositiu", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Crea un compte", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Feble", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Moderada", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Forta", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Elimina el compte", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Ens sabrà greu veure't marxar. Tens algun problema?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Sí, envia comentaris", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "No, elimina el compte", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Si us plau, autentica't per iniciar l'eliminació del compte", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirma la supressió del compte", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Aquest compte està vinculat a altres apps d'Ente, si en fas ús.\n\nLes dades pujades, a través de totes les apps d'Ente, es programaran per a la supressió, i el teu compte s'eliminarà permanentment.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Elimina", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Crea un nou compte", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Contrasenya", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirma la contrasenya", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Força de la contrasenya: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Com vas conèixer Ente? (opcional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "No fem seguiment de les instal·lacions de l'app. Ens ajudaria saber on ens has trobat!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Estic d'acord amb els termes del servei i la política de privacitat", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Termes", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Política de privacitat", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Entenc que si perdo la meva contrasenya, puc perdre les meves dades ja que les meves dades estan xifrades d'extrem a extrem.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Xifratge", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Inicia sessió", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Benvingut de nou!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "En fer clic a iniciar sessió, estic d'acord amb els termes del servei i la política de privacitat", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Sense connexió a Internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Comprova la connexió a Internet i torna-ho a intentar.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "La verificació ha fallat, intenta-ho de nou", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Recrea la contrasenya", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "El dispositiu actual no és prou potent per verificar la teva contrasenya, però podem regenerar-la d'una manera que funcioni amb tots els dispositius.\n\nSi us plau, inicia sessió utilitzant la teva clau de recuperació i regenera la teva contrasenya (pots tornar a utilitzar la mateixa si ho desitges).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Usa la clau de recuperació", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Has oblidat la contrasenya", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Canvia el correu electrònic", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verifica el correu electrònic", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Hem enviat un correu a {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Per restablir la teva contrasenya, si us plau verifica primer el teu correu electrònic.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Comprova la teva safata d'entrada (i el correu no desitjat) per completar la verificació", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Toca per introduir el codi", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Envia correu electrònic", + "resendEmail": "Reenviar correu electrònic", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "La verificació encara està pendent", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sessió caducada", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "La teva sessió ha caducat. Torna a iniciar sessió.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verificació per passkey", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Esperant verificació...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Intenta-ho de nou", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Comprova l'estat", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Inici de sessió amb TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Recupera el compte", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Configura la contrasenya", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Canvia la contrasenya", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Restableix la contrasenya", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Claus de xifratge", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Introdueix una contrasenya que puguem utilitzar per xifrar les teves dades", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Introdueix una nova contrasenya que puguem utilitzar per xifrar les teves dades", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "No guardem aquesta contrasenya, per tant, si l'oblides, no podrem desxifrar les teves dades", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Com funciona", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generant claus de xifratge...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "La contrasenya s'ha canviat amb èxit", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Tanca sessió en altres dispositius", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Si creus que algú pot saber la teva contrasenya, pots forçar tots els altres dispositius que usen el teu compte a tancar sessió.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Tancar sessió en altres dispositius", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "No tancar sessió", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generant claus d'encriptació...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continua", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Dispositiu no segur", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Ho sentim, no hem pogut generar claus segures en aquest dispositiu.\n\nSi us plau, registra't des d'un altre dispositiu.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "La clau de recuperació s'ha copiat al porta-retalls", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Clau de recuperació", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Si oblides la teva contrasenya, l'única manera de recuperar les teves dades és amb aquesta clau.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "No guardem aquesta clau, si us plau, guarda aquesta clau de 24 paraules en un lloc segur.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Fes-ho més tard", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Guarda la clau", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Clau de recuperació guardada a la carpeta Descàrregues!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "No tens clau de recuperació?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Autenticació de dos factors", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Introdueix el codi de 6 dígits de\nl'aplicació d'autenticació", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Dispositiu perdut?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Introdueix la teva clau de recuperació", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recupera", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Tancant sessió...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Immediatament", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Bloqueig de l'aplicació", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Bloqueig automàtic", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "No s'ha trobat cap bloqueig del sistema", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Per habilitar el bloqueig de dispositiu, configura un codi o bloqueig de pantalla en la configuració del sistema.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Tria entre el bloqueig predeterminat del dispositiu o un bloqueig personalitzat amb PIN o contrasenya.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Bloqueig del dispositiu", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Bloqueig amb PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Temps després del qual l'app es bloqueja quan es posa en segon pla", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Amaga el contingut", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Amaga el contingut d'aquesta app en el commutador d'apps del sistema i desactiva les captures de pantalla", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Amaga el contingut d'aquesta app en el commutador d'apps del sistema", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Massa intents incorrectes", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Toca per desbloquejar", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Segur que vols tancar la sessió?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Sí, tanca la sessió", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Si us plau, autentica't per veure els teus secrets", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Següent", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Estableix una nova contrasenya", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Introdueix el PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Estableix un nou PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirma", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Torna a introduir la contrasenya", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Torna a introduir el PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verifica la identitat", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "No reconegut. Torna-ho a provar.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Correcte", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancel·la", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Es requereix autenticació", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometria necessària", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Credencials del dispositiu requerides", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Es requereixen credencials del dispositiu", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Ves a configuració", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "L'autenticació biomètrica no està configurada al teu dispositiu. Ves a 'Configuració > Seguretat' per afegir autenticació biomètrica.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "L'autenticació biomètrica està desactivada. Bloqueja i desbloqueja la pantalla per activar-la.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "D'acord", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "El correu electrònic ja està registrat.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "El correu electrònic no està registrat.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Aquest correu electrònic ja està en ús", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Correu electrònic canviat a {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Autenticació fallida, intenta-ho de nou", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Autenticació amb èxit!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "La sessió ha caducat", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Clau de recuperació incorrecta", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "La clau de recuperació que has introduït és incorrecta", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Autenticació de dos factors restablerta amb èxit", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "El teu codi de verificació ha expirat", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Codi incorrecte", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Ho sentim, el codi que has introduït és incorrecte", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_cs.arb b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb index ce0e3e368d..ab5b183c46 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_cs.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb @@ -1,3 +1,696 @@ { - "networkHostLookUpErr": "Nelze se připojit k Ente, zkontrolujte, prosím, nastavení své sítě a kontaktujte podporu, pokud chyba přetrvává" -} + "networkHostLookUpErr": "Nelze se připojit k Ente, zkontrolujte, prosím, nastavení své sítě a kontaktujte podporu, pokud chyba přetrvává", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Nepodařilo se připojit k Ente, zkuste to po nějaké době znovu. Pokud chyba přetrvává, kontaktujte, prosím, podporu.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Vypadá to, že se něco pokazilo. Zkuste to prosím znovu po nějaké době. Pokud chyba přetrvává, kontaktujte prosím naši podporu.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Chyba", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Často kladené dotazy (FAQ)", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Kontaktovat podporu", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Zašlete své logy e-mailem", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Pošlete prosím logy na \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopírovat e-mailovou adresu", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exportovat logy", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Zrušit", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Nahlásit chybu", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Připojeno k {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Uložit", + "@save": { + "description": "Label for save button" + }, + "send": "Odeslat", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory), nebo odeslat do jiných aplikací?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "verify": "Ověřit", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Neplatná e-mailová adresa", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Prosím, zadejte platnou e-mailovou adresu.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Čekejte prosím...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Ověření hesla", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Nesprávné heslo", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Zkuste to prosím znovu", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Zadejte heslo", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Zadejte své heslo", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktivní relace", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Jejda", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Něco se pokazilo. Zkuste to, prosím, znovu", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Tato akce Vás odhlásí z tohoto zařízení!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Toto Vás odhlásí z následujícího zařízení:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Ukončit relaci?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Ukončit", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Toto zařízení", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Vytvořit účet", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Slabé", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Střední", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Silné", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Odstranit účet", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Mrzí nás, že odcházíte. Máte nějaké problémy s aplikací?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ano, poslat zpětnou vazbu", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Ne, odstranit účet", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Pro zahájení odstranění účtu se, prosím, ověřte", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Potvrdit odstranění účtu", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": " ", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Smazat", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Vytvořit nový účet", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Heslo", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Potvrzení hesla", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Síla hesla: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Jak jste se dozvěděli o Ente? (volitelné)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Ne sledujeme instalace aplikace. Pomůže nám, když nám sdělíte, kde jste nás našli!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Souhlasím s podmínkami služby a zásadami ochrany osobních údajů", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Podmínky", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Podmínky ochrany osobních údajů", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Rozumím, že při zapomenutí hesla mohu ztratit svá data, protože jsou zabezpečena koncovým šifrováním.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Šifrování", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Přihlásit se", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Vítejte zpět!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Kliknutím na přihlášení souhlasím s podmínkami služby a zásadami ochrany osobních údajů", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Žádné připojení k internetu", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Zkontrolujte, prosím, své připojení k internetu a zkuste to znovu.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Ověření selhalo, přihlaste se, prosím, znovu", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Resetovat heslo", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Aktzální zařízení není dostatečně výkonné pro ověření Vašeho hesla, ale můžeme ho regenerovat způsobem, který funguje ve všech zařízením.\n\nPřihlašte se pomocí obnovovacího klíče a znovu si vygenerujte své heslo (můžete použít opět stejné, pokud chcete).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Použít obnovovací klíč", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Zapomenuté heslo", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Změnit e-mail", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Ověřit e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Odeslali jsme e-mail na {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Pro obnovení hesla obnovte, prosím, nejprve svůj e-mail.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Pro dokončení ověření prosím zkontrolujte, prosím, svou doručenou poštu (a spamy)", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Klepnutím zadejte kód", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Odeslat e-mail", + "resendEmail": "Odeslat e-mail znovu", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Ověřování stále probíhá", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Relace vypršela", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Vaše relace vypršela. Přihlaste se, prosím, znovu.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "waitingForVerification": "Čekání na ověření...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Zkusit znovu", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Zkontrolovat stav", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Přihlášení s TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Obnovit účet", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Nastavit heslo", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Změnit heslo", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Obnovit heslo", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Šifrovací klíče", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Zadejte heslo, kterým můžeme zašifrovat Vaše data", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Zadejte nové heslo, kterým můžeme šifrovat Vaše data", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Vaše heslo neuchováváme. Pokud ho zapomenete, nemůžeme Vaše data dešifrovat", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Jak to funguje", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generování šifrovacích klíčů...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Heslo úspěšně změněno", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Odhlásit z ostatních zařízení", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Pokud si myslíte, že by někdo mohl znát Vaše heslo, můžete vynutit odhlášení ostatních zařízení používajících Váš účet.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Odhlásit z ostatních zařízení", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Neodhlašovat", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generování šifrovacích klíčů...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Pokračovat", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Nezabezpečené zařízení", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Omlouváme se, na tomto zařízení nemůžeme vygenerovat bezpečné klíče.\n\nprosím přihlaste se z jiného zařízení.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Obnovovací klíč byl zkopírován", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Obnovovací klíč", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Tento klíč je jedinou cestou pro obnovení Vašich dat, pokud zapomenete heslo.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Tento 24místný klíč neuchováváme, uschovejte ho, prosím, na bezpečném místě.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Udělat později", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Uložit klíč", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Obnovovací klíč uložen do složky Stažené soubory!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Nemáte obnovovací klíč?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Dvoufaktorové ověření", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Zadejte 6místný kód ze své autentizační aplikace", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Ztratili jste zařízení?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Zadejte svůj obnovovací klíč", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Obnovit", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Odhlašování...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Ihned", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Zámek aplikace", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatické zamykání", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Zámek systému nenalezen", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Pro aktivaci zámku zařízení si nastavte přístupový kód zařízení nebo zámek obrazovky v nastavení systému.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Vyberte si mezi zámkem obrazovky svého zařízení a vlastním zámkem obrazovky s PIN kódem nebo heslem.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Zámek zařízení", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Uzamčení na PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Interval, po kterém se aplikace běžící na pozadí uzamkne", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Skrýt obsah", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Skryje obsah aplikace ve ", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Skryje obsah aplikace při přepínání úloh", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Příliš mnoho neúspěšných pokusů", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Pro odemčení klepněte", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Opravdu se chcete odhlásit?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ano, odhlásit se", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Pro zobrazení svých tajných údajů se musíte ověřit", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Další", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Nastavit nové heslo", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Zadejte PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Nadra", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Potvrdit", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Zadejte heslo znovu", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Zadejte PIN znovu", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Ověřte svou identitu", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Nerozpoznáno. Zkuste znovu.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Úspěch", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Zrušit", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Je požadováno ověření", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Je požadováno biometrické ověření", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Jsou vyžadovány přihlašovací údaje zařízení", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Jsou vyžadovány přihlašovací údaje zařízení", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Jít do nastavení", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Na Vašem zařízení není nastaveno biometrické ověřování. Pro aktivaci běžte do 'Nastavení > Zabezpečení'.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrické ověřování není povoleno. Pro povolení zamkněte a odemkněte obrazovku.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-mail je již registrován.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-mail není registrován.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Tento e-mail je již používán", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-mail změněn na {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Ověření selhalo, zkuste to, prosím, znovu", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Ověření bylo úspěšné!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Relace vypršela", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Nesprávný obnovovací klíč", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Vámi zadaný obnovovací klíč je nesprávný", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Dvoufázové ověření bylo úspěšně obnoveno", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Váš ověřovací kód vypršel", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Nesprávný kód", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Omlouváme se, zadaný kód je nesprávný", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_da.arb b/mobile/packages/strings/lib/l10n/arb/strings_da.arb index e04451b45c..eda7fa3174 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_da.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_da.arb @@ -1,3 +1,688 @@ { - "networkHostLookUpErr": "Ude af stand til at forbinde til Ente. Tjek venligst dine netværksindstillinger og kontakt support hvis fejlen varer ved." -} + "networkHostLookUpErr": "Ude af stand til at forbinde til Ente. Tjek venligst dine netværksindstillinger og kontakt support hvis fejlen varer ved.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Ude af stand til at forbinde til Ente. Forsøg igen efter et stykke tid. Hvis fejlen varer ved, kontakt da venligst support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Det ser ud til at noget gik galt. Forsøg venligst igen efter lidt tid. Hvis fejlen varer ved, kontakt da venligst support.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Fejl", + "@error": { + "description": "Generic error title" + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Kontakt support", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Email dine logs", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Send venligst logs til {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopier email adresse", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Eksporter logs", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Afbryd", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Rapporter en fejl", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Forbindelse oprettet til {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Gem", + "@save": { + "description": "Label for save button" + }, + "send": "Send", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Vil du gemme på din enhed (Downloads mappe som udgangspunkt) eller sende til andre apps?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Vil du gemme på din enhed (Downloads mappe som udgangspunkt)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Bekræft", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Ugyldig email adresse", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Indtast en gyldig email adresse.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Vent venligst...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Bekræft adgangskode", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Forkert adgangskode", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Forsøg venligst igen", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Indtast adgangskode", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Indtast adgangskode", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktive sessioner", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Noget gik galt, forsøg venligst igen", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Dette vil logge dig ud af denne enhed!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Dette vil logge dig ud af den følgende enhed:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Afslut session?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Afslut", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Denne enhed", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Opret konto", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Svagt", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Middel", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Stærkt", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Slet konto", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Vi er kede af at se dig gå. Er du stødt på et problem?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ja, send feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nej, slet konto", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Bekræft venligst for at påbegynde sletning af konto", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Bekræft sletning af konto", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Denne konto er forbundet til andre Ente apps, hvis du benytter nogle.\n\nDine uploadede data for alle Ente apps vil blive slettet, og din konto vil blive slettet permanent.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Slet", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Opret konto", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Kodeord", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Bekræft kodeord", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Kodeordets styrke: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Hvordan hørte du om Ente? (valgfrit)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Vi tracker ikke app installeringer. Det ville hjælpe os at vide hvordan du fandt os!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Jeg er enig i betingelser for brug og privatlivspolitik", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Betingelser", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privatlivspolitik", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Jeg forstår at hvis jeg mister min adgangskode kan jeg miste mine data, da mine data er end-to-end krypteret.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Kryptering", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Log ind", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Velkommen tilbage!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Ved at logge ind godkender jeg Ente's betingelser for brug og privatlivspolitik.", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Ingen internetforbindelse", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Tjek venligst din internetforbindelse og forsøg igen.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Bekræftelse fejlede, forsøg venligst igen", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Gendan adgangskode", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Denne enhed er ikke kraftfuld nok til at bekræfte adgangskoden, men vi kan gendanne den på en måde der fungerer for alle enheder.\n\nLog venligst ind med din gendannelsesnøgle og gendan din adgangskode (du kan bruge den samme adgangskode igen hvis du ønsker).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Brug gendannelsesnøgle", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Glemt adgangskode", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Skift email adresse", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Bekræft email adresse", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Vi har sendt en email til {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "For at nulstille din adgangskode, bekræft venligst din email adresse.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Tjek venligst din indboks (og spam) for at færdiggøre verificeringen", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tryk for at indtaste kode", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Send email", + "resendEmail": "Send email igen", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Bekræftelse afventes stadig", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Session udløbet", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Din session er udløbet. Log venligst på igen.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Bekræftelse af adgangskode", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Venter på bekræftelse...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Forsøg igen", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Tjek status", + "@checkStatus": { + "description": "Check status button label" + }, + "recoverAccount": "Gendan konto", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Angiv adgangskode", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Skift adgangskode", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Nulstil adgangskode", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Krypteringsnøgler", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Indtast en adgangskode vi kan bruge til at kryptere dine data", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Indtast en ny adgangskode vi kan bruge til at kryptere dine data", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Vi gemmer ikke denne adgangskode, så hvis du glemmer den kan vi ikke dekryptere dine data", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Sådan fungerer det", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Genererer krypteringsnøgler...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Adgangskoden er blevet ændret", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Log ud af andre enheder", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Hvis du mistænker at nogen kender din adgangskode kan du tvinge alle enheder der benytter din konto til at logge ud.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Log ud af andre enheder", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Log ikke ud", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Genererer krypteringsnøgler...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Fortsæt", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Usikker enhed", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Beklager, vi kunne ikke generere sikre krypteringsnøgler på denne enhed.\n\nForsøg venligst at oprette en konto fra en anden enhed.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Gendannelsesnøgle kopieret til udklipsholderen", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Gendannelsesnøgle", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Hvis du glemmer dit kodeord er gendannelsesnøglen den eneste mulighed for at få adgang til dine data.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Vi gemmer ikke denne nøgle, gem venligst denne 24-ords nøgle et sikkert sted.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Gør det senere", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Gem nøgle", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Gendannelsesnøgle gemt i din Downloads mappe!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Ingen gendannelsesnøgle?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Tofaktorgodkendelse", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Indtast den 6-cifrede kode fra din authenticator app", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Mistet enhed?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Indtast din gendannelsesnøgle", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Gendan", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Logger ud...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Med det samme", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Låsning af app", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatisk lås", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Ingen systemlås fundet", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "For at aktivere enhedslås, indstil venligst kode eller skærmlås på din enhed i dine systemindstillinger.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Vælg mellem din enheds standard skærmlås eller skærmlås med pinkode eller adgangskode.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Enhedslås", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Låsning med pinkode", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Tid til låsning af app efter at være blevet placeret i baggrunden", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Skjul indhold", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Skjul app indhold i app-vælger og deaktiver screenshots", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Skjul app indhold i app-vælger", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "For mange forkerte forsøg", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tryk for at låse op", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Er du sikker på at du vil logge ud?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ja, log ud", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Bekræft venligst din identitet for at se dine hemmeligheder", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Næste", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Indstil ny adgangskode", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Indtast pinkode", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Indstil ny pinkode", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Bekræft", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Indtast adgangskode igen", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Indtast pinkode igen", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Bekræft identitet", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Ikke genkendt. Forsøg igen.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Succes", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Afbryd", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Godkendelse påkrævet", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometri påkrævet", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Enhedsoplysninger påkrævet", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Enhedsoplysninger påkrævet", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Gå til indstillinger", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometrisk godkendelse er ikke indstillet på din enhed. Gå til \"Indstillinger > Sikkerhed\" for at indstille biometrisk godkendelse.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrisk godkendelse er slået fra. Lås din skærm, og lås den derefter op for at aktivere det.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Denne email adresse er allerede i brug", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Email adresse ændret til {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Bekræftelse af identitet fejlede, forsøg venligst igen", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Bekræftelse af identitet lykkedes!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Session udløbet", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Forkert gendannelsesnøgle", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Den indtastede gendannelsesnøgle er ikke korrekt", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Tofaktorgodkendelse nulstillet", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Din bekræftelseskode er udløbet", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Forkert kode", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Beklager, den indtastede kode er forkert", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_de.arb b/mobile/packages/strings/lib/l10n/arb/strings_de.arb new file mode 100644 index 0000000000..fd5219b843 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_de.arb @@ -0,0 +1,704 @@ +{ + "networkHostLookUpErr": "Ente ist im Moment nicht erreichbar. Bitte überprüfen Sie Ihre Netzwerkeinstellungen. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Ente ist im Moment nicht erreichbar. Bitte versuchen Sie es später erneut. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren Sie unser Supportteam.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Fehler", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Support kontaktieren", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "E-Mail mit Logs senden", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Bitte Logs an {toEmail} senden", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "E-Mail-Adresse kopieren", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Logs exportieren", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Abbrechen", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Einen Fehler melden", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Mit {endpoint} verbunden", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Speichern", + "@save": { + "description": "Label for save button" + }, + "send": "Senden", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern oder an andere Apps senden?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Gib deine neue E-Mail-Adresse ein", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-Mail", + "@email": { + "description": "Email field label" + }, + "verify": "Verifizieren", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Ungültige E-Mail-Adresse", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Bitte geben Sie eine gültige E-Mail-Adresse ein.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Bitte warten...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Passwort überprüfen", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Falsches Passwort", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Bitte versuchen Sie es erneut", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Passwort eingeben", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Geben Sie Ihr Passwort ein", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktive Sitzungen", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Hoppla", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Ein Fehler ist aufgetreten, bitte erneut versuchen", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Dadurch werden Sie von diesem Gerät abgemeldet!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Dadurch werden Sie vom folgendem Gerät abgemeldet:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Sitzung beenden?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Beenden", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Dieses Gerät", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Konto erstellen", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Schwach", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Mittel", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Stark", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Konto löschen", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Es tut uns leid, dass Sie gehen. Haben Sie ein Problem?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ja, Feedback senden", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nein, Konto löschen", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Bitte authentifizieren Sie sich, um die Kontolöschung einzuleiten", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Kontolöschung bestätigen", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Dieses Konto ist mit anderen Ente-Apps verknüpft, falls Sie welche verwenden.\n\nIhre hochgeladenen Daten werden in allen Ente-Apps zur Löschung vorgemerkt und Ihr Konto wird endgültig gelöscht.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Löschen", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Neues Konto erstellen", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Passwort", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Bestätigen Sie das Passwort", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Passwortstärke: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Ich stimme den Nutzerbedingungen und Datenschutzbestimmungen zu", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Bedingungen", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Datenschutzbestimmungen", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Ich verstehe, dass der Verlust meines Passworts zum Verlust meiner Daten führen kann, denn diese sind Ende-zu-Ende verschlüsselt.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Verschlüsselung", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Einloggen", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Willkommen zurück!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Durch das Klicken auf den Login-Button, stimme ich den Nutzungsbedingungen und den Datenschutzbestimmungen zu", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Keine Internetverbindung", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie erneut.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verifizierung fehlgeschlagen, bitte versuchen Sie es erneut", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Neues Passwort erstellen", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Das benutzte Gerät ist nicht leistungsfähig genug das Passwort zu prüfen. Wir können es aber neu erstellen damit es auf jedem Gerät funktioniert. \n\nBitte loggen sie sich mit ihrem Wiederherstellungsschlüssel ein und erstellen sie ein neues Passwort (Sie können das selbe Passwort wieder verwenden, wenn sie möchten).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Wiederherstellungsschlüssel verwenden", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Passwort vergessen", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "E-Mail ändern", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "E-Mail-Adresse verifizieren", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Wir haben eine E-Mail an {email} gesendet", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Um Ihr Passwort zurückzusetzen, verifizieren Sie bitte zuerst Ihre E-Mail-Adresse.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Bitte überprüfe deinen E-Mail-Posteingang (und Spam), um die Verifizierung abzuschließen", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Antippen, um den Code einzugeben", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "E-Mail senden", + "resendEmail": "E-Mail erneut senden", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Verifizierung steht noch aus", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sitzung abgelaufen", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Passkey Authentifizierung", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Warte auf Bestätigung...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Noch einmal versuchen", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Status überprüfen", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Mit TOTP anmelden", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Konto wiederherstellen", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Passwort setzen", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Passwort ändern", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Passwort zurücksetzen", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Verschlüsselungsschlüssel", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Geben Sie ein Passwort ein, mit dem wir Ihre Daten verschlüsseln können", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Geben Sie ein neues Passwort ein, mit dem wir Ihre Daten verschlüsseln können", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Wir speichern dieses Passwort nicht. Wenn Sie es vergessen, können wir Ihre Daten nicht entschlüsseln", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "So funktioniert's", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generierung von Verschlüsselungsschlüsseln...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Passwort erfolgreich geändert", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Von anderen Geräten abmelden", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Falls Sie denken, dass jemand Ihr Passwort kennen könnte, können Sie alle anderen Geräte forcieren, sich von Ihrem Konto abzumelden.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Andere Geräte abmelden", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Nicht abmelden", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generierung von Verschlüsselungsschlüsseln...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Weiter", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Unsicheres Gerät", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Es tut uns leid, wir konnten keine sicheren Schlüssel auf diesem Gerät generieren.\n\nBitte registrieren Sie sich auf einem anderen Gerät.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Wiederherstellungsschlüssel in die Zwischenablage kopiert", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Wiederherstellungsschlüssel", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Sollten sie ihr Passwort vergessen, dann ist dieser Schlüssel die einzige Möglichkeit ihre Daten wiederherzustellen.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Wir speichern diesen Schlüssel nicht. Sichern Sie diesen Schlüssel bestehend aus 24 Wörtern an einem sicheren Platz.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Auf später verschieben", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Schlüssel speichern", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Wiederherstellungsschlüssel im Downloads-Ordner gespeichert!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Kein Wiederherstellungsschlüssel?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Zwei-Faktor-Authentifizierung", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Gerät verloren?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Geben Sie Ihren Wiederherstellungsschlüssel ein", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Wiederherstellen", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Wird abgemeldet...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Sofort", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "App-Sperre", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatisches Sperren", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Keine Systemsperre gefunden", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Um die Gerätesperre zu aktivieren, richte bitte einen Gerätepasscode oder eine Bildschirmsperre in den Systemeinstellungen ein.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Wähle zwischen dem Standard-Sperrbildschirm deines Gerätes und einem eigenen Sperrbildschirm mit PIN oder Passwort.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Gerätesperre", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN-Sperre", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Zeit, nach der die App gesperrt wird, nachdem sie in den Hintergrund verschoben wurde", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Inhalte verstecken", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Versteckt Inhalte der App beim Wechseln zwischen Apps und deaktiviert Screenshots", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Versteckt Inhalte der App beim Wechseln zwischen Apps", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Zu viele fehlerhafte Versuche", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Zum Entsperren antippen", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Sind sie sicher, dass sie sich ausloggen möchten?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ja ausloggen", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Bitte authentifizieren, um Ihren Wiederherstellungscode anzuzeigen", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Weiter", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Neues Passwort festlegen", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "PIN eingeben", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Neue PIN festlegen", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Bestätigen", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Passwort erneut eingeben", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "PIN erneut eingeben", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Identität bestätigen", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Nicht erkannt. Versuchen Sie es erneut.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Erfolgreich", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Abbrechen", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Authentifizierung erforderlich", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometrie erforderlich", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Geräteanmeldeinformationen erforderlich", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Geräteanmeldeinformationen erforderlich", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Zu den Einstellungen", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Auf Ihrem Gerät ist keine biometrische Authentifizierung eingerichtet. Gehen Sie zu 'Einstellungen > Sicherheit', um die biometrische Authentifizierung hinzuzufügen.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Die biometrische Authentifizierung ist deaktiviert. Bitte sperren und entsperren Sie Ihren Bildschirm, um sie wieder zu aktivieren.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-Mail ist bereits registriert.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-Mail-Adresse nicht registriert.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Diese E-Mail-Adresse wird bereits verwendet", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-Mail-Adresse geändert zu {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Authentifizierung fehlgeschlagen, bitte erneut versuchen", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Authentifizierung erfolgreich!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sitzung abgelaufen", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Falscher Wiederherstellungs-Schlüssel", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Der eingegebene Wiederherstellungs-Schlüssel ist ungültig", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Zwei-Faktor-Authentifizierung (2FA) erfolgreich zurückgesetzt", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Ihr Bestätigungscode ist abgelaufen", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Falscher Code", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Leider ist der eingegebene Code falsch", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_el.arb b/mobile/packages/strings/lib/l10n/arb/strings_el.arb index 001c0474de..cd1bd4f6ed 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_el.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_el.arb @@ -1,3 +1,692 @@ { - "networkHostLookUpErr": "Δεν είναι δυνατή η σύνδεση με το Ente, ελέγξτε τις ρυθμίσεις του δικτύου σας και επικοινωνήστε με την υποστήριξη αν το σφάλμα παραμένει." -} + "networkHostLookUpErr": "Δεν είναι δυνατή η σύνδεση με το Ente, ελέγξτε τις ρυθμίσεις του δικτύου σας και επικοινωνήστε με την υποστήριξη αν το σφάλμα παραμένει.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Δεν είναι δυνατή η σύνδεση με το Ente, παρακαλώ προσπαθήστε ξανά μετά από λίγο. Εάν το σφάλμα παραμένει, παρακαλούμε επικοινωνήστε με την υποστήριξη.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Φαίνεται ότι κάτι πήγε στραβά. Παρακαλώ προσπαθήστε ξανά μετά από λίγο. Αν το σφάλμα παραμένει, παρακαλούμε επικοινωνήστε με την ομάδα υποστήριξης μας.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Σφάλμα", + "@error": { + "description": "Generic error title" + }, + "ok": "Οκ", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Συχνές Ερωτήσεις", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Επικοινωνήστε με την υποστήριξη", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Στείλτε με email τα αρχεία καταγραφής σας", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Παρακαλώ στείλτε τα αρχεία καταγραφής σας στο \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Αντιγραφή διεύθυνσης email", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Εξαγωγή αρχείων καταγραφής", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Ακύρωση", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Αναφορά Σφάλματος", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Συνδεδεμένο στο {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Αποθήκευση", + "@save": { + "description": "Label for save button" + }, + "send": "Αποστολή", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή) ή να το στείλετε σε άλλες εφαρμογές;", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή);", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Επαλήθευση", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Μη έγκυρη διεύθυνση email", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Παρακαλώ περιμένετε…", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Επαλήθευση κωδικού πρόσβασης", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Λάθος κωδικός πρόσβασης", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Παρακαλώ δοκιμάστε ξανά", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Εισάγετε κωδικό πρόσβασης", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Εισάγετε τον κωδικό πρόσβασης σας", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Ενεργές συνεδρίες", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ουπς", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Κάτι πήγε στραβά, παρακαλώ προσπαθήστε ξανά", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Αυτό θα σας αποσυνδέσει από αυτή τη συσκευή!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Αυτό θα σας αποσυνδέσει από την ακόλουθη συσκευή:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Τερματισμός συνεδρίας;", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Τερματισμός", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Αυτή η συσκευή", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Δημιουργία λογαριασμού", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Αδύναμος", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Μέτριος", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Δυνατός", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Διαγραφή λογαριασμού", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Λυπόμαστε που σας βλέπουμε να φεύγετε. Αντιμετωπίζετε κάποιο πρόβλημα;", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ναι, αποστολή σχολίων", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Όχι, διαγραφή λογαριασμού", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να ξεκινήσετε τη διαγραφή λογαριασμού", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Επιβεβαίωση διαγραφής λογαριασμού", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Αυτός ο λογαριασμός είναι συνδεδεμένος με άλλες εφαρμογές Ente, εάν χρησιμοποιείτε κάποια.\n\nΤα δεδομένα σας, σε όλες τις εφαρμογές Ente, θα προγραμματιστούν για διαγραφή και ο λογαριασμός σας θα διαγραφεί οριστικά.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Διαγραφή", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Δημιουργία νέου λογαριασμού", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Κωδικόs πρόσβασης", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Επιβεβαίωση κωδικού πρόσβασης", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Ισχύς κωδικού πρόσβασης: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Πώς ακούσατε για το Ente; (προαιρετικό)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Δεν παρακολουθούμε εγκαταστάσεις εφαρμογών. Θα βοηθούσε αν μας είπατε πού μας βρήκατε!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Όροι", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Πολιτική Απορρήτου", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Καταλαβαίνω ότι αν χάσω τον κωδικό μου μπορεί να χάσω τα δεδομένα μου αφού τα δεδομένα μου είναι από άκρο-σε-άκρο κρυπτογραφημένα.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Kρυπτογράφηση", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Σύνδεση", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Καλωσορίσατε και πάλι!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Κάνοντας κλικ στη σύνδεση, συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Χωρίς σύνδεση στο διαδίκτυο", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Παρακαλούμε ελέγξτε τη σύνδεσή σας στο διαδίκτυο και προσπαθήστε ξανά.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Η επαλήθευση απέτυχε, παρακαλώ προσπαθήστε ξανά", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Επαναδημιουργία κωδικού πρόσβασης", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Η τρέχουσα συσκευή δεν είναι αρκετά ισχυρή για να επαληθεύσει τον κωδικό πρόσβασής σας, αλλά μπορούμε να τον αναδημιουργήσουμε με έναν τρόπο που λειτουργεί με όλες τις συσκευές.\n\nΠαρακαλούμε συνδεθείτε χρησιμοποιώντας το κλειδί ανάκτησης και αναδημιουργήστε τον κωδικό πρόσβασής σας (μπορείτε να χρησιμοποιήσετε ξανά τον ίδιο αν το επιθυμείτε).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Χρήση κλειδιού ανάκτησης", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Ξέχασα τον κωδικό πρόσβασης σας", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Αλλαγή email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Επαλήθευση email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Έχουμε στείλει ένα μήνυμα στο {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Για να επαναφέρετε τον κωδικό πρόσβασής σας, επαληθεύστε πρώτα το email σας.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Παρακαλώ ελέγξτε τα εισερχόμενά σας (και τα ανεπιθύμητα) για να ολοκληρώσετε την επαλήθευση", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Πατήστε για να εισάγετε τον κωδικό", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Αποστολή email", + "resendEmail": "Επανάληψη αποστολής email", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Η επαλήθευση εξακολουθεί να εκκρεμεί", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Η συνεδρία έληξε", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Η συνεδρία σας έληξε. Παρακαλώ συνδεθείτε ξανά.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Επιβεβαίωση κλειδιού πρόσβασης", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Αναμονή για επαλήθευση...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Προσπαθήστε ξανά", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Έλεγχος κατάστασης", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Είσοδος με TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Ανάκτηση λογαριασμού", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Ορισμός κωδικού πρόσβασης", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Αλλαγή κωδικού πρόσβασής", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Επαναφορά κωδικού πρόσβασης", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Κλειδιά κρυπτογράφησης", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Εισάγετε έναν κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για την κρυπτογράφηση των δεδομένων σας", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Εισάγετε ένα νέο κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για να κρυπτογραφήσουμε τα δεδομένα σας", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Δεν αποθηκεύουμε αυτόν τον κωδικό πρόσβασης, οπότε αν τον ξεχάσετε δεν μπορούμε να αποκρυπτογραφήσουμε τα δεδομένα σας", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Πώς λειτουργεί", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Δημιουργία κλειδιών κρυπτογράφησης...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Ο κωδικός πρόσβασης άλλαξε επιτυχώς", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Αποσύνδεση από άλλες συσκευές", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Αν νομίζετε ότι κάποιος μπορεί να γνωρίζει τον κωδικό πρόσβασής σας, μπορείτε να αναγκάσετε όλες τις άλλες συσκευές που χρησιμοποιούν το λογαριασμό σας να αποσυνδεθούν.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Αποσύνδεση άλλων συσκευών", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Μην αποσυνδεθείτε", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Δημιουργία κλειδιών κρυπτογράφησης…", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Συνέχεια", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Μη ασφαλής συσκευή", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Συγγνώμη, δεν μπορέσαμε να δημιουργήσουμε ασφαλή κλειδιά σε αυτήν τη συσκευή.\n\nπαρακαλώ εγγραφείτε από μια διαφορετική συσκευή.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Το κλειδί ανάκτησης αντιγράφηκε στο πρόχειρο", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Κλειδί ανάκτησης", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Εάν ξεχάσετε τον κωδικό πρόσβασής σας, ο μόνος τρόπος για να ανακτήσετε τα δεδομένα σας είναι με αυτό το κλειδί.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Δεν αποθηκεύουμε αυτό το κλειδί, παρακαλώ αποθηκεύστε αυτό το κλειδί 24 λέξεων σε μια ασφαλή τοποθεσία.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Κάντε το αργότερα", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Αποθήκευση κλειδιού", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Το κλειδί ανάκτησης αποθηκεύτηκε στο φάκελο Λήψεις!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Χωρίς κλειδί ανάκτησης;", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Αυθεντικοποίηση δύο παραγόντων", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Εισάγετε τον 6ψήφιο κωδικό από \nτην εφαρμογή αυθεντικοποίησης", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Χαμένη συσκευή;", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Εισάγετε το κλειδί ανάκτησης σας", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Ανάκτηση", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Αποσύνδεση…", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Άμεσα", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Κλείδωμα εφαρμογής", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Αυτόματο κλείδωμα", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Δεν βρέθηκε κλείδωμα συστήματος", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Για να ενεργοποιήσετε το κλείδωμα της συσκευής, παρακαλώ ρυθμίστε τον κωδικό πρόσβασης της συσκευής ή το κλείδωμα οθόνης στις ρυθμίσεις του συστήματός σας.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Επιλέξτε ανάμεσα στην προεπιλεγμένη οθόνη κλειδώματος της συσκευής σας και σε μια προσαρμοσμένη οθόνη κλειδώματος με ένα PIN ή έναν κωδικό πρόσβασης.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Κλείδωμα συσκευής", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Κλείδωμα καρφιτσωμάτων", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Χρόνος μετά τον οποίο η εφαρμογή κλειδώνει μετά την τοποθέτηση στο παρασκήνιο", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Απόκρυψη περιεχομένου", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Απόκρυψη περιεχομένου εφαρμογής στην εναλλαγή εφαρμογών και απενεργοποίηση στιγμιότυπων οθόνης", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Απόκρυψη περιεχομένου εφαρμογών στην εναλλαγή εφαρμογών", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Πάρα πολλές εσφαλμένες προσπάθειες", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Πατήστε για ξεκλείδωμα", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ναι, αποσύνδεση", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε τα μυστικά σας", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Επόμενο", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Ορίστε νέο κωδικό πρόσβασης", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Εισαγωγή PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Ορίστε νέο PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Επιβεβαίωση", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Πληκτρολογήστε ξανά τον κωδικό πρόσβασης", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Πληκτρολογήστε ξανά το PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Επαλήθευση ταυτότητας", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Δεν αναγνωρίζεται. Δοκιμάστε ξανά.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Επιτυχία", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Ακύρωση", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Απαιτείται έλεγχος ταυτότητας", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Απαιτούνται βιομετρικά", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Απαιτούνται στοιχεία συσκευής", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Απαιτούνται στοιχεία συσκευής", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Μετάβαση στις ρυθμίσεις", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Η βιομετρική πιστοποίηση δεν έχει ρυθμιστεί στη συσκευή σας. Μεταβείτε στις 'Ρυθμίσεις > Ασφάλεια' για να προσθέσετε βιομετρική ταυτοποίηση.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Η βιομετρική ταυτοποίηση είναι απενεργοποιημένη. Παρακαλώ κλειδώστε και ξεκλειδώστε την οθόνη σας για να την ενεργοποιήσετε.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "ΟΚ", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Αυτό το email είναι ήδη σε χρήση", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Το email άλλαξε σε {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Αποτυχία ελέγχου ταυτότητας, παρακαλώ προσπαθήστε ξανά", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Επιτυχής έλεγχος ταυτότητας!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Η συνεδρία έληξε", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Εσφαλμένο κλειδί ανάκτησης", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Το κλειδί ανάκτησης που εισάγατε είναι εσφαλμένο", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Η αυθεντικοποίηση δύο παραγόντων επαναφέρθηκε επιτυχώς", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Ο κωδικός επαλήθευσης σας έχει λήξει", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Εσφαλμένος κωδικός", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Λυπούμαστε, ο κωδικός που εισαγάγατε είναι εσφαλμένος", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index aa8dfc0ae5..3a574c2fb0 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -91,22 +91,7 @@ "@notAvailable": { "description": "Not available text" }, - "enteLogsPrefix": "ente-logs-", - "@enteLogsPrefix": { - "description": "Prefix for log file names" - }, - "logsDirectoryName": "logs", - "@logsDirectoryName": { - "description": "Name of logs directory" - }, - "logsZipFileName": "logs.zip", - "@logsZipFileName": { - "description": "Name of zipped log file" - }, - "zipFileExtension": "zip", - "@zipFileExtension": { - "description": "File extension for zip files" - }, + "reportABug": "Report a bug", "@reportABug": { "description": "Label for reporting a bug" diff --git a/mobile/packages/strings/lib/l10n/arb/strings_es.arb b/mobile/packages/strings/lib/l10n/arb/strings_es.arb index ccfec794b3..33fc9fbb9a 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_es.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_es.arb @@ -1,9 +1,700 @@ { - "networkHostLookUpErr": "No se puede conectar a Ente, por favor verifica tu configuración de red y ponte en contacto con el soporte si el error persiste.", + "networkHostLookUpErr": "No se puede conectar a Ente. Por favor, comprueba tu configuración de red y ponte en contacto con el soporte técnico si el error persiste.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, "networkConnectionRefusedErr": "No se puede conectar a Ente. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con el soporte técnico.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Parece que algo salió mal. Por favor, vuelve a intentarlo pasado un tiempo. Si el error persiste, ponte en contacto con nuestro equipo de soporte.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, "error": "Error", + "@error": { + "description": "Generic error title" + }, "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, "faq": "Preguntas Frecuentes", - "contactSupport": "Ponerse en contacto con el equipo de soporte" -} + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Ponerse en contacto con el equipo de soporte", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Envíe sus registros por correo electrónico", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Por favor, envíe los registros a {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copiar dirección de correo electrónico", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exportar registros", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Cancelar", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Reportar un error", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Conectado a {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Guardar", + "@save": { + "description": "Label for save button" + }, + "send": "Enviar", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto) o enviarlo a otras aplicaciones?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Correo electrónico", + "@email": { + "description": "Email field label" + }, + "verify": "Verificar", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Dirección de correo electrónico no válida", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Por favor, introduce una dirección de correo electrónico válida.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Por favor, espere...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verificar contraseña", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Contraseña incorrecta", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Por favor, inténtalo de nuevo", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Introduzca la contraseña", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Introduce tu contraseña", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sesiones activas", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Algo ha ido mal, por favor, inténtelo de nuevo", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "¡Esto cerrará la sesión de este dispositivo!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Esto cerrará la sesión del siguiente dispositivo:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "¿Terminar sesión?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Terminar", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Este dispositivo", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Crear cuenta", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Poco segura", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Moderada", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Segura", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Eliminar cuenta", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Lamentamos que te vayas. ¿Estás teniendo algún problema?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Sí, enviar comentarios", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "No, eliminar cuenta", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Por favor, autentícate para iniciar la eliminación de la cuenta", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirmar eliminación de la cuenta", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Esta cuenta está vinculada a otras aplicaciones de Ente, si utilizas alguna. \n\nSe programará la eliminación de los datos cargados en todas las aplicaciones de Ente, y tu cuenta se eliminará permanentemente.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Borrar", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Crear cuenta nueva", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Contraseña", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirmar contraseña", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Fortaleza de la contraseña: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "¿Cómo conoció Ente? (opcional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "No rastreamos la instalación de las aplicaciones. ¡Nos ayudaría si nos dijera dónde nos encontró!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Estoy de acuerdo con los términos del servicio y la política de privacidad", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Términos", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Política de Privacidad", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Entiendo que si pierdo mi contraseña podría perder mis datos, ya que mis datos están cifrados de extremo a extremo.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Cifrado", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Iniciar sesión", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "¡Te damos la bienvenida otra vez!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Al hacer clic en iniciar sesión, acepto los términos de servicio y la política de privacidad", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "No hay conexión a Internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Compruebe su conexión a Internet e inténtelo de nuevo.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verificación fallida, por favor inténtalo de nuevo", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Recrear contraseña", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "El dispositivo actual no es lo suficientemente potente para verificar su contraseña, pero podemos regenerarla de manera que funcione con todos los dispositivos.\n\nPor favor inicie sesión usando su clave de recuperación y regenere su contraseña (puede volver a utilizar la misma si lo desea).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Usar clave de recuperación", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Olvidé mi contraseña", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Cambiar correo electrónico", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verificar correo electrónico", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Hemos enviado un correo electrónico a {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Para restablecer tu contraseña, por favor verifica tu correo electrónico primero.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Por favor revisa tu bandeja de entrada (y spam) para completar la verificación", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Toca para introducir el código", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Enviar correo electrónico", + "resendEmail": "Reenviar correo electrónico", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "La verificación todavía está pendiente", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "La sesión ha expirado", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Tu sesión ha expirado. Por favor, vuelve a iniciar sesión.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verificación de clave de acceso", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Esperando verificación...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Inténtelo de nuevo", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Comprobar estado", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Inicio de sesión con TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Recuperar cuenta", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Establecer contraseña", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Cambiar contraseña", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Restablecer contraseña", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Claves de cifrado", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Introduzca una contraseña que podamos usar para cifrar sus datos", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Introduzca una contraseña nueva que podamos usar para cifrar sus datos", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "No almacenamos esta contraseña, así que si la olvidas, no podremos descifrar tus datos", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Cómo funciona", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generando claves de cifrado...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Contraseña cambiada correctamente", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Cerrar sesión en otros dispositivos", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Si crees que alguien puede conocer tu contraseña, puedes forzar a todos los demás dispositivos que usen tu cuenta a cerrar la sesión.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Cerrar la sesión en otros dispositivos", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "No cerrar la sesión", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generando claves de cifrado...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continuar", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Dispositivo inseguro", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Lo sentimos, no hemos podido generar claves seguras en este dispositivo.\n\nRegístrate desde un dispositivo diferente.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Clave de recuperación copiada al portapapeles", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Clave de recuperación", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Si olvidas tu contraseña, la única forma de recuperar tus datos es con esta clave.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Nosotros no almacenamos esta clave, por favor guarda esta clave de 24 palabras en un lugar seguro.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Hacer esto más tarde", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Guardar clave", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "¡Clave de recuperación guardada en la carpeta Descargas!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "¿No tienes la clave de recuperación?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Autenticación de dos factores", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Ingrese el código de seis dígitos de su aplicación de autenticación", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "¿Dispositivo perdido?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Introduce tu clave de recuperación", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recuperar", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Cerrando sesión...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Inmediatamente", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Bloqueo de aplicación", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Bloqueo automático", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Bloqueo del sistema no encontrado", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Para activar el bloqueo de la aplicación, por favor configura el código de acceso del dispositivo o el bloqueo de pantalla en los ajustes de tu sistema.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Elija entre la pantalla de bloqueo por defecto de su dispositivo y una pantalla de bloqueo personalizada con un PIN o contraseña.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Bloqueo del dispositivo", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Bloqueo con PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Tiempo tras el cual la aplicación se bloquea después de ser colocada en segundo plano", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Ocultar contenido", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Oculta el contenido de la aplicación en el selector de aplicaciones y desactiva las capturas de pantalla", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Ocultar el contenido de la aplicación en el selector de aplicaciones", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Demasiados intentos incorrectos", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Toca para desbloquear", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "¿Seguro que quieres cerrar la sesión?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Sí, cerrar la sesión", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Por favor, autentícate para ver tus secretos", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Siguiente", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Establece una nueva contraseña", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Ingresa el PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Establecer nuevo PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirmar", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Reescribe tu contraseña", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Reescribe tu PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verificar identidad", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "No reconocido. Inténtalo de nuevo.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Autenticación exitosa", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancelar", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Se necesita autenticación biométrica", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Se necesita autenticación biométrica", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Se necesitan credenciales de dispositivo", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Se necesitan credenciales de dispositivo", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Ir a Ajustes", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "La autenticación biométrica no está configurada en tu dispositivo. Ve a 'Ajustes > Seguridad' para configurar la autenticación biométrica.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "La autenticación biométrica está deshabilitada. Por favor bloquea y desbloquea la pantalla para habilitarla.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Aceptar", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Correo electrónico ya registrado.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Correo electrónico no registrado.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Este correo electrónico ya está en uso", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Correo electrónico cambiado a {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Error de autenticación, por favor inténtalo de nuevo", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "¡Autenticación exitosa!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "La sesión ha expirado", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Clave de recuperación incorrecta", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "La clave de recuperación introducida es incorrecta", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Autenticación de doble factor restablecida con éxito", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Tu código de verificación ha expirado", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Código incorrecto", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Lo sentimos, el código que has introducido es incorrecto", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_et.arb b/mobile/packages/strings/lib/l10n/arb/strings_et.arb new file mode 100644 index 0000000000..3a6ab9a777 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_et.arb @@ -0,0 +1,306 @@ +{ + "error": "Viga", + "@error": { + "description": "Generic error title" + }, + "ok": "Sobib", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "KKK", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Võtke ühendust klienditoega", + "@contactSupport": { + "description": "Contact support button label" + }, + "cancel": "Katkesta", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Teata veast", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "save": "Salvesta", + "@save": { + "description": "Label for save button" + }, + "send": "Saada", + "@send": { + "description": "Label for send button" + }, + "enterNewEmailHint": "Sisesta oma uus e-posti aadress", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-post", + "@email": { + "description": "Email field label" + }, + "verify": "Kinnita", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Vigane e-posti aadress", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Palun sisesta korrektne e-posti aadress.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Palun oota...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Korda salasõna", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Vale salasõna", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Palun proovi uuesti", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Sisesta salasõna", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Sisesta oma salasõna", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "oops": "Vaat kus lops!", + "@oops": { + "description": "Oops error title" + }, + "weakStrength": "Nõrk", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Keskmine", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Tugev", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Kustuta kasutajakonto", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Meil on kahju, et soovid lahkuda. Kas sul tekkis mõni viga või probleem?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Jah, saadan tagasisidet", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Ei, kustuta kasutajakonto", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Kasutajakonto kustutamiseks palun tuvasta end", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "delete": "Kustuta", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Loo uus kasutajakonto", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Salasõna", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Korda salasõna", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Salasõna tugevus: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "termsOfServicesTitle": "Kasutustingimused", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privaatsusreeglid", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Ma saan aru, et salasõna kaotamisel kaotan ka ligipääsu oma andmetele - minu andmed on ju läbivalt krüptitud.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Krüptimine", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Logi sisse", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Tere tulemast tagasi!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Sisselogdes nõustun kasutustingimustega ja privaatsusreeglitega", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "recreatePasswordTitle": "Loo salasõna uuesti", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "useRecoveryKey": "Kasuta taastevõtit", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Unustasin salasõna", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Muuda e-posti aadressi", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Kinnita e-posti aadress", + "@verifyEmail": { + "description": "Verify email title" + }, + "sendEmail": "Saada e-kiri", + "tryAgain": "Proovi uuesti", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Kontrolli olekut", + "@checkStatus": { + "description": "Check status button label" + }, + "recoverAccount": "Taasta oma kasutajakonto", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Sisesta salasõna", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Muuda salasõna", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Lähtesta salasõna", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Krüptovõtmed", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "howItWorks": "Kuidas see töötab", + "@howItWorks": { + "description": "How it works button label" + }, + "passwordChangedSuccessfully": "Salasõna muutmine õnnestus", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "generatingEncryptionKeysTitle": "Loon krüptovõtmeid...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "recoveryKeyCopiedToClipboard": "Taastevõti on kopeeritud lõikelauale", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Taastevõti", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Kui unustad oma salasõna, siis see krüptovõti on ainus võimalus sinu andmete taastamiseks.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "saveKey": "Salvesta võti", + "@saveKey": { + "description": "Save key button label" + }, + "noRecoveryKeyTitle": "Sul pole taastevõtit?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "enterCodeHint": "Sisesta oma autentimisrakendusest\n6-numbriline kood", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Kas kaotasid oma seadme?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Sisesta oma taastevõti", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Taasta", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Väljalogimine...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "areYouSureYouWantToLogout": "Kas oled kindel, et soovid välja logida?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Jah, logi välja", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "setNewPassword": "Sisesta uus salasõna", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Sisesta PIN-kood", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Määra uus PIN-kood", + "@setNewPin": { + "description": "Set new PIN title" + }, + "reEnterPassword": "Sisesta salasõna uuesti", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "emailAlreadyRegistered": "E-posti aadress on juba registreeritud.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-posti aadress pole registreeritud.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "sessionExpired": "Sessioon on aegunud", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_fa.arb b/mobile/packages/strings/lib/l10n/arb/strings_fa.arb new file mode 100644 index 0000000000..f1f1cc05a9 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_fa.arb @@ -0,0 +1,620 @@ +{ + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "به نظر می‌رسد مشکلی پیش آمده است. لطفا بعد از مدتی دوباره تلاش کنید. اگر همچنان با خطا مواجه می‌شوید، لطفا با تیم پشتیبانی ما ارتباط برقرار کنید.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "خطا", + "@error": { + "description": "Generic error title" + }, + "ok": "تایید", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "سوالات متداول", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "ارتباط با پشتیبانی", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "لاگ‌های خود را ایمیل کنید", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "لطفا لاگ‌ها را به ایمیل زیر ارسال کنید \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "کپی آدرس ایمیل", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "صدور لاگ‌ها", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "لغو", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "گزارش یک اشکال", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "متصل شده به {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "ذخیره", + "@save": { + "description": "Label for save button" + }, + "send": "ارسال", + "@send": { + "description": "Label for send button" + }, + "email": "ایمیل", + "@email": { + "description": "Email field label" + }, + "verify": "تایید", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "آدرس ایمیل نامعتبر است", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "لطفا یک آدرس ایمیل معتبر وارد کنید.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "لطفا صبر کنید...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "تایید رمز عبور", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "رمز عبور نادرست", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "لطفا دوباره تلاش کنید", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "رمز عبور را وارد کنید", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "رمز عبور خود را وارد کنید", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "نشست های فعال", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "اوه", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "مشکلی پیش آمده، لطفا دوباره تلاش کنید", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "این کار شما را از این دستگاه خارج می‌کند!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "با این کار شما از دستگاه زیر خارج می‌شوید:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "خروچ دستگاه؟", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "خروج", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "این دستگاه", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "ایجاد حساب کاربری", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "ضعیف", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "متوسط", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "قوی", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "حذف حساب کاربری", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "از رفتن شما متاسفیم. آیا با مشکلی روبرو هستید؟", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "بله، ارسال بازخورد", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "خیر، حساب کاربری را حذف کن", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "لطفا جهت شروع فرآیند حذف حساب کاربری، اعتبارسنجی کنید", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "تایید حذف حساب کاربری", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "delete": "حذف", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "ایجاد حساب کاربری جدید", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "رمز عبور", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "تایید رمز عبور", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "قدرت رمز عبور: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "از کجا در مورد Ente شنیدی؟ (اختیاری)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "ما نصب برنامه را ردیابی نمی‌کنیم. اگر بگویید کجا ما را پیدا کردید، به ما کمک می‌کند!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "با شرایط استفاده از خدمات و سیاست حفظ حریم خصوصی موافقت می‌کنم", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "شرایط و ضوابط", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "سیاست حفظ حریم خصوصی", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "می‌دانم که اگر رمز عبور خود را گم کنم، از آنجایی که اطلاعات من رمزگذاری سرتاسری شده است، ممکن است اطلاعاتم را از دست بدهم.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "رمزگذاری", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "ورود", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "خوش آمدید!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "با کلیک روی ورود، با شرایط استفاده از خدمات و سیاست حفظ حریم خصوصی موافقت می‌کنم", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "نبود اتصال اینترنت", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "لطفا اتصال اینترنت خود را بررسی کنید و دوباره امتحان کنید.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "تایید ناموفق بود، لطفا مجددا تلاش کنید", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "بازتولید رمز عبور", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "دستگاه فعلی جهت تایید رمز عبور شما به اندازه کافی قدرتمند نیست، اما ما میتوانیم آن را به گونه ای بازتولید کنیم که با همه دستگاه‌ها کار کند.\n\nلطفا با استفاده از کلید بازیابی خود وارد شوید و رمز عبور خود را دوباره ایجاد کنید (در صورت تمایل می‌توانید دوباره از همان رمز عبور استفاده کنید).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "از کلید بازیابی استفاده کنید", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "فراموشی رمز عبور", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "تغییر ایمیل", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "تایید ایمیل", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "ما یک ایمیل به {email} ارسال کرده‌ایم", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "برای تنظیم مجدد رمز عبور، لطفا ابتدا ایمیل خود را تایید کنید.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "لطفا صندوق ورودی (و هرزنامه) خود را برای تایید کامل بررسی کنید", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "برای وارد کردن کد ضربه بزنید", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "ارسال ایمیل", + "resendEmail": "ارسال مجدد ایمیل", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "تأییدیه هنوز در انتظار است", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "نشست منقضی شده است", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "نشست شما منقضی شده. لطفا دوباره وارد شوید.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "تایید کردن پسکی", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "درانتظار تأییدیه...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "دوباره امتحان کنید", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "بررسی وضعیت", + "@checkStatus": { + "description": "Check status button label" + }, + "recoverAccount": "بازیابی حساب کاربری", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "تنظیم پسورد", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "تغییر رمز عبور", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "بازنشانی رمز عبور", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "کلیدهای رمزنگاری", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "رمز عبوری را وارد کنید که بتوانیم از آن برای رمزگذاری اطلاعات شما استفاده کنیم", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "رمز عبور جدیدی را وارد کنید که بتوانیم از آن برای رمزگذاری اطلاعات شما استفاده کنیم", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "howItWorks": "چگونه کار می‌کند", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "در حال تولید کلیدهای رمزگذاری...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "رمز عبور با موفقیت تغییر کرد", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "از دستگاه های دیگر خارج شوید", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherDevices": "از دستگاه های دیگر خارج شوید", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "خارج نشوید", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "در حال تولید کلید‌های رمزگذاری...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "ادامه", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "دستگاه ناامن", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "با عرض پوزش، ما نمی‌توانیم کلیدهای امن را در این دستگاه تولید کنیم.\n\nلطفا از دستگاه دیگری ثبت نام کنید.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "کلید بازیابی به حافظه موقت کپی شد", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "کلید بازیابی", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "اگر رمز عبور خود را فراموش کرده‌اید، این کد تنها راهی است که با آن می‌توانید اطلاعات خود را بازیابی کنید.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "ما این کلید را ذخیره نمی‌کنیم، لطفا این کلید ۲۴ کلمه‌ای را در مکانی امن ذخیره کنید.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "بعداً انجام شود", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "ذخیره کلید", + "@saveKey": { + "description": "Save key button label" + }, + "noRecoveryKeyTitle": "کلید بازیابی ندارید؟", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "احراز هویت دو مرحله‌ای", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "کد تایید ۶ رقمی را از برنامه\nاحراز هویت خود وارد کنید", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "دستگاه را گم کرده‌اید؟", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "کلید بازیابی خود را وارد کنید", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "بازیابی", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "در حال خروج از سیستم...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "فوری", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "قفل برنامه", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "قفل خودکار", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "هیج قبل سیستمی پیدا نشد", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLock": "قفل دستگاه", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "پین قفل", + "@pinLock": { + "description": "PIN lock option title" + }, + "hideContent": "پنهان کردن محتوا", + "@hideContent": { + "description": "Hide content setting title" + }, + "tapToUnlock": "برای باز کردن قفل ضربه بزنید", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "آیا مطمئنید که می‌خواهید خارج شوید؟", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "بله، خروج", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "لطفا جهت دیدن راز های خود احراز هویت کنید", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "بعدی", + "@next": { + "description": "Next button label" + }, + "enterPin": "پین را وارد کنید", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "پین جدید انتخاب کنید", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "تایید", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "رمز عبور را مجدداً وارد کنید", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "پین را مجدداً وارد کنید", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "تایید هویت", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "شناخته نشد. دوباره امتحان کنید.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "موفقیت", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "لغو", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "احراز هویت لازم است", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "بیومتریک لازم است", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "اعتبار دستگاه لازم است", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "اعتبار دستگاه لازم است", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "به تنظیمات بروید", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "iOSOkButton": "تأیید", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "این ایمیل درحال استفاده است", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "ایمیل عوض شد به {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "احراز هویت ناموفق بود، لطفا دوباره تلاش کنید", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "احراز هویت موفق آمیز!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "نشست منقضی شده است", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "کلید بازیابی درست نیست", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "کلید بازیابی که وارد کردید درست نیست", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "احراز هویت دو مرحله با موفقیت بازنشانی شد", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "کد تایید شما باطل شد", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "کد اشتباه", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "معظرت میخوام، کدی که شما وارد کردید اشتباه است", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_fi.arb b/mobile/packages/strings/lib/l10n/arb/strings_fi.arb new file mode 100644 index 0000000000..6cebf2c8d6 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_fi.arb @@ -0,0 +1,271 @@ +{ + "error": "Virhe", + "@error": { + "description": "Generic error title" + }, + "ok": "Selvä", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Usein kysyttyä", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Ota yhteyttä käyttötukeen", + "@contactSupport": { + "description": "Contact support button label" + }, + "cancel": "Keskeytä", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Ilmoita virhetoiminnosta", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "save": "Tallenna", + "@save": { + "description": "Label for save button" + }, + "send": "Lähetä", + "@send": { + "description": "Label for send button" + }, + "email": "Sähköposti", + "@email": { + "description": "Email field label" + }, + "verify": "Vahvista", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Epäkelpo sähköpostiosoite", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Syötä kelpoisa sähköpostiosoite.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Odota hetki...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Vahvista salasana", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Salasana on väärin", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Yritä uudestaan", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Syötä salasana", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Syötä salasanasi", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "oops": "Hupsista", + "@oops": { + "description": "Oops error title" + }, + "createAccount": "Luo tili", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Heikko salasana", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Kohtalainen salasana", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Vahva salasana", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Poista tili", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Olemme pahoillamme että lähdet keskuudestamme. Kohtasitko käytössä jonkin ongelman?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Kyllä, lähetä palautetta", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "En, poista tili", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Ole hyvä ja tee todennus käynnistääksesi tilisi poistoprosessin", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "delete": "Poista", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Luo uusi tili", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Salasana", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Vahvista salasana", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "encryption": "Salaus", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Kirjaudu sisään", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Tervetuloa takaisin!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "useRecoveryKey": "Käytä palautusavainta", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Olen unohtanut salasanani", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "vaihda sähköpostiosoitteesi", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Vahvista sähköpostiosoite", + "@verifyEmail": { + "description": "Verify email title" + }, + "sendEmail": "Lähetä sähköpostia", + "passkeyAuthTitle": "Avainkoodin vahvistus", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "recoverAccount": "Palauta tilisi", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Luo salasana", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Vaihda salasana", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Nollaa salasana", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Salausavaimet", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "generatingEncryptionKeys": "Luodaan salausavaimia...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Salasana vaihdettu onnistuneesti", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "generatingEncryptionKeysTitle": "Luodaan salausavaimia...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Jatka", + "@continueLabel": { + "description": "Continue button label" + }, + "recoveryKeyCopiedToClipboard": "Palautusavain kopioitu leikepöydälle", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Palautusavain", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Jos unohdat salasanasi, ainoa tapa palauttaa tietosi on tällä avaimella.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Emme tallenna tätä avainta, ole hyvä ja tallenna tämä 24 sanan avain turvalliseen paikkaan.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Tee tämä myöhemmin", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Tallenna avain", + "@saveKey": { + "description": "Save key button label" + }, + "noRecoveryKeyTitle": "Ei palautusavainta?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Kaksivaiheinen vahvistus", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Syötä 6-merkkinen koodi varmennussovelluksestasi", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Kadonnut laite?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Syötä palautusavaimesi", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Palauta", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Kirjaudutaan ulos...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "yesLogout": "Kyllä, kirjaudu ulos", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "androidBiometricSuccess": "Kirjautuminen onnistui", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Peruuta", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "goToSettings": "Mene asetuksiin", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "sessionExpired": "Istunto on vanheutunut", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_fr.arb b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb index 95e1d02e90..ede2965d7d 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_fr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb @@ -1,9 +1,704 @@ { "networkHostLookUpErr": "Impossible de se connecter à Ente, veuillez vérifier vos paramètres réseau et contacter le support si l'erreur persiste.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, "networkConnectionRefusedErr": "Impossible de se connecter à Ente, veuillez réessayer après un certain temps. Si l'erreur persiste, veuillez contacter le support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, "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.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, "error": "Erreur", + "@error": { + "description": "Generic error title" + }, "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, "faq": "FAQ", - "contactSupport": "Contacter le support" -} + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contacter le support", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Envoyez vos logs par e-mail", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Envoyez les logs à {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copier l’adresse e-mail", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exporter les journaux", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Annuler", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Signaler un bug", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Connecté à {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Sauvegarder", + "@save": { + "description": "Label for save button" + }, + "send": "Envoyer", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ou l'envoyer à d'autres applications ?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Saisissez votre nouvelle adresse email", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "verify": "Vérifier", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Adresse e-mail invalide", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Veuillez saisir une adresse e-mail valide.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Veuillez patienter...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Vérifier le mot de passe", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Mot de passe incorrect", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Veuillez réessayer", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Saisissez le mot de passe", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Entrez votre mot de passe", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sessions actives", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Oups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Quelque chose s'est mal passé, veuillez recommencer", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Cela vous déconnectera de cet appareil !", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Cela vous déconnectera de l'appareil suivant :", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Quitter la session ?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Quitter", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Cet appareil", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Créer un compte", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Faible", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Modéré", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Fort", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Supprimer le compte", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Nous sommes désolés de vous voir partir. Rencontrez-vous un problème ?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Oui, envoyer un commentaire", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Non, supprimer le compte", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Veuillez vous authentifier pour débuter la suppression du compte", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirmer la suppression du compte", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Ce compte est lié à d'autres applications ente, si vous en utilisez une.\n\nVos données téléchargées, dans toutes les applications ente, seront planifiées pour suppression, et votre compte sera définitivement supprimé.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Supprimer", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Créer un nouveau compte", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Mot de passe", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirmer le mot de passe", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Force du mot de passe : {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Comment avez-vous entendu parler de Ente? (facultatif)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Nous ne suivons pas les installations d'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "J'accepte les conditions d'utilisation et la politique de confidentialité", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Conditions", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Politique de confidentialité", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Je comprends que si je perds mon mot de passe, je risque de perdre mes données puisque celles-ci sont chiffrées de bout en bout.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Chiffrement", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Connexion", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Bon retour parmi nous !", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "En cliquant sur \"Connexion\", j'accepte les conditions d'utilisation et la politique de confidentialité", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Aucune connexion internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Veuillez vérifier votre connexion internet puis réessayer.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "La vérification a échouée, veuillez réessayer", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Recréer le mot de passe", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "L'appareil actuel n'est pas assez puissant pour vérifier votre mot de passe, donc nous avons besoin de le régénérer une fois d'une manière qu'il fonctionne avec tous les périphériques.\n\nVeuillez vous connecter en utilisant votre clé de récupération et régénérer votre mot de passe (vous pouvez utiliser le même si vous le souhaitez).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Utiliser la clé de récupération", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Mot de passe oublié", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Modifier l'e-mail", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Vérifier l'e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Nous avons envoyé un mail à {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Pour réinitialiser votre mot de passe, veuillez d'abord vérifier votre e-mail.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Veuillez consulter votre boîte de courriels (et les spam) pour compléter la vérification", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Appuyez pour entrer un code", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Envoyer un e-mail", + "resendEmail": "Renvoyer le courriel", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "La vérification est toujours en attente", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Session expirée", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Votre session a expiré. Veuillez vous reconnecter.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Vérification du code d'accès", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "En attente de vérification...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Réessayer", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Vérifier le statut", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Se connecter avec un code TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Récupérer un compte", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Définir le mot de passe", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Modifier le mot de passe", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Réinitialiser le mot de passe", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Clés de chiffrement", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Entrez un mot de passe que nous pouvons utiliser pour chiffrer vos données", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Entrez un nouveau mot de passe que nous pouvons utiliser pour chiffrer vos données", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Nous ne stockons pas ce mot de passe. Si vous l'oubliez, nous ne pourrons pas déchiffrer vos données", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Comment ça fonctionne", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Génération des clés de chiffrement...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Le mot de passe a été modifié avec succès", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Déconnexion des autres appareils", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Si vous pensez que quelqu'un connaît peut-être votre mot de passe, vous pouvez forcer tous les autres appareils utilisant votre compte à se déconnecter.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Déconnecter les autres appareils", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Ne pas se déconnecter", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Génération des clés de chiffrement...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continuer", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Appareil non sécurisé", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "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.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Clé de récupération copiée dans le presse-papiers", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Clé de récupération", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Nous ne stockons pas cette clé, veuillez enregistrer cette clé de 24 mots dans un endroit sûr.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Plus tard", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Enregistrer la clé", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Clé de récupération enregistrée dans le dossier Téléchargements !", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Pas de clé de récupération ?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Authentification à deux facteurs", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Entrez le code à 6 chiffres de votre application d'authentification", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Appareil perdu ?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Saisissez votre clé de récupération", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Restaurer", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Deconnexion...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Immédiatement", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Verrouillage d'application", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Verrouillage automatique", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Aucun verrou système trouvé", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "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.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Choisissez entre l'écran de verrouillage par défaut de votre appareil et un écran de verrouillage par code PIN ou mot de passe personnalisé.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Verrouillage de l'appareil", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Verrouillage par code PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Délai après lequel l'application se verrouille une fois qu'elle a été mise en arrière-plan", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Masquer le contenu", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Masque le contenu de l'application sur le menu et désactive les captures d'écran", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Masque le contenu de l'application sur le menu", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Trop de tentatives incorrectes", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Appuyer pour déverrouiller", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Êtes-vous sûr de vouloir vous déconnecter ?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Oui, se déconnecter", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Veuillez vous authentifier pour voir vos souvenirs", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Suivant", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Définir un nouveau mot de passe", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Saisir le code PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Définir un nouveau code PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirmer", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Ressaisir le mot de passe", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Ressaisir le code PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Vérifier l’identité", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Non reconnu. Veuillez réessayer.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Parfait", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Annuler", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Authentification requise", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Empreinte digitale requise", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Identifiants de l'appareil requis", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Identifiants de l'appareil requis", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Allez dans les paramètres", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "L'authentification biométrique n'est pas configurée sur votre appareil. Allez dans 'Paramètres > Sécurité' pour l'ajouter.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "L'authentification biométrique est désactivée. Veuillez verrouiller et déverrouiller votre écran pour l'activer.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Ok", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-mail déjà enregistré.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-mail non enregistré.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Cette adresse mail est déjà utilisé", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "L'e-mail a été changé en {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "L'authentification a échouée, veuillez réessayer", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Authentification réussie!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Session expirée", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Clé de récupération non valide", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "La clé de récupération que vous avez entrée est incorrecte", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "L'authentification à deux facteurs a été réinitialisée avec succès ", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Votre code de vérification a expiré", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Code non valide", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Le code que vous avez saisi est incorrect", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_gu.arb b/mobile/packages/strings/lib/l10n/arb/strings_gu.arb new file mode 100644 index 0000000000..b62a73a638 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_gu.arb @@ -0,0 +1,90 @@ +{ + "ok": "સારું", + "@ok": { + "description": "Generic OK button label" + }, + "contactSupport": "સહાયતા માટે સંપર્ક કરો", + "@contactSupport": { + "description": "Contact support button label" + }, + "cancel": "રદ કરો", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "બગની જાણ કરો", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "email": "ઇમેઇલ", + "@email": { + "description": "Email field label" + }, + "pleaseWait": "કૃપા કરીને રાહ જુવો...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "પાસવર્ડ ચકાસો", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "ખોટો પાસવર્ડ", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "enterPassword": "પાસવર્ડ દાખલ કરો", + "@enterPassword": { + "description": "Enter password field label" + }, + "delete": "કાઢી નાખો", + "@delete": { + "description": "Delete button label" + }, + "welcomeBack": "ફરી તમારુ સ્વાગત છે!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "useRecoveryKey": "પુનઃપ્રાપ્તિ કીનો ઉપયોગ કરો", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "changeEmail": "ઈ - મેઈલ બદલો", + "@changeEmail": { + "description": "Change email button label" + }, + "generatingEncryptionKeysTitle": "એન્ક્રિપ્શન ચાવીઓ જનરેટ કરી રહ્યાં છીએ...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "recoveryKey": "પુનઃપ્રાપ્તિ ચાવી", + "@recoveryKey": { + "description": "Recovery key label" + }, + "loggingOut": "લૉગ આઉટ થઈ રહ્યું છે...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "androidBiometricSuccess": "સફળતા", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "રદ કરો", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "પ્રમાણીકરણ જરૂરી છે", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "goToSettings": "સેટિંગ માં જાઓ", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "iOSOkButton": "બરાબર", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "sessionExpired": "સત્ર સમાપ્ત થયુ", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_he.arb b/mobile/packages/strings/lib/l10n/arb/strings_he.arb new file mode 100644 index 0000000000..fc6e0fbcbd --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_he.arb @@ -0,0 +1,478 @@ +{ + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "נראה שמשהו לא פעל כשורה. אנא נסה שוב אחרי כמה זמן. אם הבעיה ממשיכה, אנא צור קשר עם צוות התמיכה שלנו.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "שגיאה", + "@error": { + "description": "Generic error title" + }, + "ok": "אוקיי", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "שאלות נפוצות", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "צור קשר עם התמיכה", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "שלח באימייל את הלוגים שלך", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "אנא שלחו את הלוגים האלו ל-{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "העתק כתובת דוא\"ל", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "ייצוא לוגים", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "בטל", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "דווח על באג", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "save": "שמור", + "@save": { + "description": "Label for save button" + }, + "send": "שלח", + "@send": { + "description": "Label for send button" + }, + "email": "דוא\"ל", + "@email": { + "description": "Email field label" + }, + "verify": "אמת", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "כתובת דוא״ל לא תקינה", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "אנא הכנס כתובת דוא\"ל תקינה.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "אנא המתן...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "אמת סיסמא", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "סיסמא לא נכונה", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "אנא נסה שנית", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "הזן את הסיסמה", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "הכנס סיסמא", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "חיבורים פעילים", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "אופס", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "משהו השתבש, אנא נסה שנית", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "זה ינתק אותך במכשיר זה!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "זה ינתק אותך מהמכשיר הבא:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "סיים חיבור?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "סיים", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "מכשיר זה", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "צור חשבון", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "חלש", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "מתון", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "חזק", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "מחק חשבון", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "אנו מצטערים שאתה עוזב. האם יש בעיות שאתה חווה?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "כן, שלח משוב", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "לא, מחק את החשבון", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "אנא אמת על מנת להתחיל את מחיקת החשבון שלך", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "אישור מחיקת חשבון", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "delete": "למחוק", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "צור חשבון חדש", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "סיסמא", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "אמת סיסמא", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "חוזק הסיסמא: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "signUpTerms": "אני מסכים לתנאי שירות ולמדיניות הפרטיות", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "תנאים", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "מדיניות פרטיות", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "אני מבין שאם אאבד את הסיסמא, אני עלול לאבד את המידע שלי מכיוון שהמידע שלי מוצפן מקצה אל קצה.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "הצפנה", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "התחבר", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "ברוך שובך!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "על ידי לחיצה על התחברות, אני מסכים לתנאי שירות ולמדיניות הפרטיות", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "אין חיבור לאינטרנט", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "אנא בדוק את חיבור האינטרנט שלך ונסה שוב.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "אימות נכשל, אנא נסה שנית", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "צור סיסמא מחדש", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "המכשיר הנוכחי אינו חזק מספיק כדי לאמת את הסיסמא שלך, אבל אנחנו יכולים ליצור מחדש בצורה שתעבוד עם כל המכשירים.\n\nאנא התחבר בעזרת המפתח שחזור שלך וצור מחדש את הסיסמא שלך (אתה יכול להשתמש באותה אחת אם אתה רוצה).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "השתמש במפתח שחזור", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "שכחתי סיסמה", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "שנה דוא\"ל", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "אימות דוא\"ל", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "שלחנו דוא\"ל ל{email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "כדי לאפס את הסיסמא שלך, אנא אמת את האימייל שלך קודם.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "אנא בדוק את תיבת הדואר שלך (והספאם) כדי להשלים את האימות", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "הקש כדי להזין את הקוד", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "שלח אימייל", + "resendEmail": "שלח דוא\"ל מחדש", + "@resendEmail": { + "description": "Resend email button label" + }, + "tryAgain": "נסה שוב", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "בדוק סטטוס", + "@checkStatus": { + "description": "Check status button label" + }, + "recoverAccount": "שחזר חשבון", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "הגדר סיסמא", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "שנה סיסמה", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "איפוס סיסמה", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "מפתחות ההצפנה", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "הזן סיסמא כדי שנוכל להצפין את המידע שלך", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "הכנס סיסמא חדשה כדי שנוכל להצפין את המידע שלך", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "אנחנו לא שומרים את הסיסמא הזו, לכן אם אתה שוכח אותה, אנחנו לא יכולים לפענח את המידע שלך", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "איך זה עובד", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "יוצר מפתחות הצפנה...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "הסיסמה הוחלפה בהצלחה", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "generatingEncryptionKeysTitle": "יוצר מפתחות הצפנה...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "המשך", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "מכשיר בלתי מאובטח", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "אנחנו מצטערים, לא הצלחנו ליצור מפתחות מאובטחים על מכשיר זה.\n\nאנא הירשם ממכשיר אחר.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "מפתח השחזור הועתק ללוח", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "מפתח שחזור", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "אם אתה שוכח את הסיסמא שלך, הדרך היחידה שתוכל לשחזר את המידע שלך היא עם המפתח הזה.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "אנחנו לא מאחסנים את המפתח הזה, אנא שמור את המפתח 24 מילים הזה במקום בטוח.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "עשה זאת מאוחר יותר", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "שמור מפתח", + "@saveKey": { + "description": "Save key button label" + }, + "noRecoveryKeyTitle": "אין מפתח שחזור?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "אימות דו-שלבי", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "הכנס את הקוד בעל 6 ספרות מתוך\nאפליקציית האימות שלך", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "איבדת את המכשיר?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "הזן את מפתח השחזור שלך", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "שחזר", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "מתנתק...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "areYouSureYouWantToLogout": "האם את/ה בטוח/ה שאת/ה רוצה לצאת?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "כן, התנתק", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "confirm": "אשר", + "@confirm": { + "description": "Confirm button label" + }, + "thisEmailIsAlreadyInUse": "כתובת דואר אלקטרוני זאת כבר נמצאת בשימוש", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "אימייל שונה ל-{newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "אימות נכשל, אנא נסה שוב", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "אימות הצליח!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "זמן החיבור הסתיים", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "מפתח שחזור שגוי", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "המפתח שחזור שהזנת שגוי", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "אימות דו-שלבי אופס בהצלחה", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "קוד האימות שלך פג תוקף", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "קוד שגוי", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "אנו מתנצלים, אבל הקוד שהזנת איננו נכון", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_hi.arb b/mobile/packages/strings/lib/l10n/arb/strings_hi.arb new file mode 100644 index 0000000000..8bac8f273f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_hi.arb @@ -0,0 +1,102 @@ +{ + "ok": "ठीक है", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "अक्सर किये गए सवाल", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "सपोर्ट टीम से संपर्क करें", + "@contactSupport": { + "description": "Contact support button label" + }, + "cancel": "रद्द करें", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "बग रिपोर्ट करें", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "verify": "सत्यापित करें", + "@verify": { + "description": "Verify button label" + }, + "pleaseWait": "कृपया प्रतीक्षा करें...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "पासवर्ड सत्यापित करें", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "ग़लत पासवर्ड", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "कृपया पुन: प्रयास करें", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterYourPasswordHint": "अपना पासवर्ड दर्ज करें", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "oops": "ओह", + "@oops": { + "description": "Oops error title" + }, + "delete": "हटाएं", + "@delete": { + "description": "Delete button label" + }, + "welcomeBack": "आपका पुनः स्वागत है!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "useRecoveryKey": "रिकवरी कुंजी का उपयोग करें", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "पासवर्ड भूल गए", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "ईमेल बदलें", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "ईमेल सत्यापित करें", + "@verifyEmail": { + "description": "Verify email title" + }, + "generatingEncryptionKeysTitle": "एन्क्रिप्शन कुंजियाँ उत्पन्न हो रही हैं...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "recoveryKey": "पुनःप्राप्ति कुंजी", + "@recoveryKey": { + "description": "Recovery key label" + }, + "twoFactorAuthTitle": "दो-चरणीय प्रमाणीकरण |", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "loggingOut": "लॉग आउट हो रहा है...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "emailAlreadyRegistered": "ईमेल पहले से ही पंजीकृत है।", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "ईमेल पंजीकृत नहीं है।", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "sessionExpired": "सत्र की अवधि समाप्त", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_hu.arb b/mobile/packages/strings/lib/l10n/arb/strings_hu.arb new file mode 100644 index 0000000000..cb3fc544f4 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_hu.arb @@ -0,0 +1,704 @@ +{ + "networkHostLookUpErr": "Nem lehet csatlakozni az Ente-hez. Kérjük, ellenőrizze a hálózati beállításokat, és ha a hiba továbbra is fennáll, forduljon az ügyfélszolgálathoz.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Nem lehet csatlakozni az Ente-hez, próbálja újra egy idő után. Ha a hiba továbbra is fennáll, forduljon az ügyfélszolgálathoz.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Úgy tűnik, valami hiba történt. Kérjük, próbálja újra egy idő után. Ha a hiba továbbra is fennáll, forduljon ügyfélszolgálatunkhoz.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Hiba", + "@error": { + "description": "Generic error title" + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "GY. I. K.", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Lépj kapcsolatba az Ügyfélszolgálattal", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "E-mailben küldje el naplóit", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Kérjük, küldje el a naplókat erre az e-mail címre\n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "E-mail cím másolása", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Naplófájlok exportálása", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Mégse", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Hiba bejelentése", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Csatlakozva a következőhöz: {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Mentés", + "@save": { + "description": "Label for save button" + }, + "send": "Küldés", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "El szeretné menteni ezt a tárhelyére (alapértelmezés szerint a Letöltések mappába), vagy elküldi más alkalmazásoknak?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "El szeretné menteni ezt a tárhelyére (alapértelmezés szerint a Letöltések mappába)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Add meg az új e-mail címed", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "verify": "Hitelesítés", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Érvénytelen e-mail cím", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Kérjük, adjon meg egy érvényes e-mail címet.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Kérem várjon...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Jelszó megerősítése", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Érvénytelen jelszó", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Kérjük, próbálja meg újra", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Adja meg a jelszót", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Adja meg a jelszavát", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktív munkamenetek", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Hoppá", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Hiba történt. Kérjük, próbálkozz újra", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Ezzel kijelentkezik erről az eszközről!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Ezzel kijelentkezik a következő eszközről:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Megszakítja a munkamenetet?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Befejezés", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Ez az eszköz", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Felhasználó létrehozás", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Gyenge", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Mérsékelt", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Erős", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Fiók törlése", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Szomorúan tapasztaljuk. Problémába ütköztél?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Igen, visszajelzés küldése", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Fiók végleges törlése", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Kérjük, hitelesítse magát a fiók törlésének kezdeményezéséhez", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Fiók törlésének megerősítése", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Ez a fiók össze van kapcsolva más Ente-alkalmazásokkal, ha használ ilyet.\n\nA feltöltött adataid törlését ütemezzük az összes Ente alkalmazásban, és a fiókod véglegesen törlésre kerül.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Törlés", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Új fiók létrehozása", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Jelszó", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Jelszó megerősítése", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Jelszó erőssége: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Honnan hallottál Ente-ről? (opcionális)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Nem követjük nyomon az alkalmazástelepítéseket. Segítene, ha elmondaná, hol talált ránk!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Elfogadom az szolgáltatási feltételeket és az adatvédelmi irányelveket", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Használati feltételek", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Adatvédelmi irányelvek", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Tudomásul veszem, hogy ha elveszítem a jelszavamat, elveszíthetem az adataimat, mivel adataim végponttól végpontig titkosítva vannak.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Titkosítás", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Bejelentkezés", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Köszöntjük ismét!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "A bejelentkezés gombra kattintva elfogadom az szolgáltatási feltételeket és az adatvédelmi irányelveket", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Nincs internet kapcsolat", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Kérjük, ellenőrizze az internetkapcsolatát, és próbálja meg újra.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Az ellenőrzés sikertelen, próbálja újra", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Új jelszó létrehozása", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "A jelenlegi eszköz nem elég erős a jelszavának ellenőrzéséhez, de újra tudjuk úgy generálni, hogy az minden eszközzel működjön.\n\nKérjük, jelentkezzen be helyreállítási kulcsával, és állítsa be újra jelszavát (ha szeretné, újra használhatja ugyanazt).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Helyreállítási kulcs használata", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Elfelejtett jelszó", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "E-mail cím módosítása", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "E-mail cím megerősítése", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "E-mailt küldtünk a következő címre: {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Jelszava visszaállításához először igazolja e-mail-címét.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Kérjük, ellenőrizze beérkező leveleit (és spam mappát) az ellenőrzés befejezéséhez", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Koppintson a kód beírásához", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "E-mail küldése", + "resendEmail": "E-mail újraküldése", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Az ellenőrzés még függőben van", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Lejárt a munkamenet", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "A munkameneted lejárt. Kérem lépjen be újra.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Álkulcs megerősítése", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Várakozás az ellenőrzésre...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Próbáld újra", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Állapot ellenőrzése", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Bejelentkezés TOTP-vel", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Fiók visszaállítása", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Jelszó beállítás", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Jelszó módosítás", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Jelszó visszaállítás", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Titkosító kulcsok", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Adjon meg egy jelszót, amellyel titkosíthatjuk adatait", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Adjon meg egy új jelszót, amellyel titkosíthatjuk adatait", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Ezt a jelszót nem tároljuk, így ha elfelejti, nem tudjuk visszafejteni adatait", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Hogyan működik", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Titkosító kulcsok generálása...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "A jelszó sikeresen meg lett változtatva", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Jelentkezzen ki más eszközökről", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Ha úgy gondolja, hogy valaki ismeri jelszavát, kényszerítheti a fiókját használó összes többi eszközt a kijelentkezésre.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Jelentkezzen ki a többi eszközről", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Ne jelentkezzen ki", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Titkosítási kulcs generálása...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Folytatás", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Nem biztonságos eszköz", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Sajnáljuk, nem tudtunk biztonságos kulcsokat generálni ezen az eszközön.\n\nkérjük, regisztráljon egy másik eszközről.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "A helyreállítási kulcs a vágólapra másolva", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Visszaállítási kulcs", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Ha elfelejti jelszavát, csak ezzel a kulccsal tudja visszaállítani adatait.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Ezt a kulcsot nem tároljuk, kérjük, őrizze meg ezt a 24 szavas kulcsot egy biztonságos helyen.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Később", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Mentés", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "A helyreállítási kulcs a Letöltések mappába mentve!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Nincs helyreállítási kulcs?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Kétlépcsős hitelesítés (2FA)", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Írja be a 6 számjegyű kódot a hitelesítő alkalmazásból", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Elveszett a készüléked?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Visszaállító kód megadása", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Visszaállít", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Kijelentkezés...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Azonnal", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Alkalmazások zárolása", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatikus lezárás", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nem található rendszerzár", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Az eszközzár engedélyezéséhez állítsa be az eszköz jelszavát vagy a zárképernyőt a rendszerbeállításokban.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Válasszon az eszköz alapértelmezett zárolási képernyője és a PIN-kóddal vagy jelszóval rendelkező egyéni zárolási képernyő között.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Eszköz lezárás", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN feloldás", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Az az idő, amely elteltével az alkalmazás zárolásra kerül, miután a háttérbe került", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Tartalom elrejtése", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Elrejti az alkalmazás tartalmát az alkalmazásváltóban, és letiltja a képernyőképeket", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Elrejti az alkalmazás tartalmát az alkalmazásváltóban", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Túl sok helytelen próbálkozás", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Koppintson a feloldáshoz", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Biztos benne, hogy kijelentkezik?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Igen, kijelentkezés", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "A titkos kulcsok megtekintéséhez hitelesítse magát", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Következő", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Új jelszó beállítása", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "PIN kód megadása", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Új PIN kód beállítása", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Megerősítés", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Írja be újra a jelszót", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Írja be újra a PIN-kódot", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Személyazonosság ellenőrzése", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Nem felismerhető. Próbáld újra.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Sikeres", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Mégse", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Hitelesítés szükséges", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometria szükséges", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Az eszköz hitelesítő adatai szükségesek", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Az eszköz hitelesítő adatai szükségesek", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Beállítások megnyitása", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "A biometrikus hitelesítés nincs beállítva az eszközön. A biometrikus hitelesítés hozzáadásához lépjen a 'Beállítások > Biztonság' menüpontra.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "A biometrikus hitelesítés ki van kapcsolva. Az engedélyezéséhez zárja le és oldja fel a képernyőt.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Ez az e-mai cím már regisztrálva van.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Ez az e-mail cím nincs regisztrálva.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Ez az e-mail már használatban van", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Az e-mail cím módosítva erre: {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "A hitelesítés sikertelen, próbálja újra", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Sikeres hitelesítés!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "A munkamenet lejárt", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Helytelen helyreállítási kulcs", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "A megadott helyreállítási kulcs helytelen", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "A kétfaktoros hitelesítés visszaállítása sikeres", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Ez az ellenőrző kód lejárt", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Helytelen kód", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Sajnáljuk, a megadott kód helytelen", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_id.arb b/mobile/packages/strings/lib/l10n/arb/strings_id.arb index d721d9f922..6a7c621b5a 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_id.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_id.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Tidak dapat terhubung ke Ente. Mohon periksa kembali koneksi internet Anda dan hubungi tim bantuan kami jika galat masih ada." -} + "networkHostLookUpErr": "Tidak dapat terhubung ke Ente. Mohon periksa kembali koneksi internet Anda dan hubungi tim bantuan kami jika galat masih ada.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Sepertinya ada yang salah. Mohon coba lagi setelah beberapa waktu. Jika galat masih ada, Anda dapat menghubungi tim bantuan kami.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Sepertinya ada yang salah. Mohon coba lagi setelah beberapa waktu. Jika galat masih ada, Anda dapat menghubungi tim bantuan kami.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Kesalahan", + "@error": { + "description": "Generic error title" + }, + "ok": "Oke", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Pertanyaan yang sering ditanyakan", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Hubungi dukungan", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Kirimkan log Anda melalui surel", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Mohon kirim log ke {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Salin alamat surel", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Ekspor log", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Batal", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Laporkan bug", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Terhubung ke {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Simpan", + "@save": { + "description": "Label for save button" + }, + "send": "Kirim", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads) atau Anda ingin kirimkan ke aplikasi lain?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads)", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Masukkan alamat email baru anda", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Verifikasi", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Alamat email tidak valid", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Harap masukkan alamat email yang valid.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Mohon tunggu...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verifikasi kata sandi", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Kata sandi salah", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Harap coba lagi", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Masukkan kata sandi", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Masukkan kata sandi Anda", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sesi aktif", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Ada yang salah. Mohon coba kembali", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Langkah ini akan mengeluarkan Anda dari gawai ini!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Langkah ini akan mengeluarkan Anda dari gawai berikut:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Akhiri sesi?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Akhiri", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Gawai ini", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Buat akun", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Lemah", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Sedang", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Kuat", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Hapus akun", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Kami akan merasa kehilangan Anda. Apakah Anda menghadapi masalah?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ya, kirim umpan balik", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Tidak, hapus akun", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Harap autentikasi untuk memulai penghapusan akun", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Konfirmasikan penghapusan akun", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Akun ini terhubung dengan aplikasi Ente yang lain (jika Anda pakai).\n\nData yang Anda unggah di seluruh aplikasi Ente akan dijadwalkan untuk dihapus. Akun Anda juga akan dihapus secara permanen.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Hapus", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Buat akun baru", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Kata Sandi", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Konfirmasi kata sandi", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Tingkat kekuatan kata sandi: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Dari mana Anda menemukan Ente? (opsional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Kami tidak melacak penginstalan aplikasi kami. Akan sangat membantu kami bila Anda memberitahu kami dari mana Anda mengetahui Ente!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Ketentuan", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Kebijakan Privasi", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Saya mengerti bahwa jika saya lupa kata sandi saya, data saya dapat hilang karena data saya terenkripsi secara end-to-end.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Enkripsi", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Masuk akun", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Selamat datang kembali!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Dengan menekan masuk akun, saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Tiada koneksi internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Mohon periksa koneksi internet Anda dan coba kembali.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Gagal memverifikasi. Mohon coba lagi", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Membuat kembali kata sandi", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Gawai Anda saat ini tidak dapat memverifikasi kata sandi Anda. Namun, kami dapat membuat ulang dengan cara yang dapat digunakan pada semua gawai.\n\nMohon masuk log dengan kunci pemulihan dan buat ulang kata sandi Anda (kata sandi yang sama diperbolehkan).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Gunakan kunci pemulihan", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Lupa kata sandi", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Ubah alamat email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verifikasi email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Kami telah mengirimkan sebuah posel ke {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Untuk mengatur ulang kata sandi, mohon verifikasi surel Anda terlebih dahulu.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Mohon cek", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Ketuk untuk memasukkan kode", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Kirim surel", + "resendEmail": "Kirim ulang surel", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Verifikasi tertunda", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sesi sudah berakhir", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Sesi Anda sudah berakhir. Mohon masuk log kembali.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verifikasi passkey", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Menantikan verifikasi...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Coba lagi", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Periksa status", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Masuk menggunakan TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Pulihkan akun", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Atur kata sandi", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Ubah kata sandi", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Atur ulang kata sandi", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Kunci enkripsi", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Masukkan kata sandi yang dapat kami gunakan untuk mengenkripsi data Anda", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Masukkan kata sandi baru yang dapat kami gunakan untuk mengenkripsi data Anda", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Kami tidak menyimpan kata sandi Anda. Jika Anda lupa, kami tidak dapat mendekripsi data Anda", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Cara kerjanya", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Sedang membuat kunci enkripsi...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Kata sandi sukses diubah", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Keluar dari gawai yang lain", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Jika Anda pikir seseorang mungkin mengetahui kata sandi Anda, Anda dapat mengeluarkan akun Anda pada semua gawai", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Keluar akun pada gawai yang lain", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Jangan keluar", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Sedang membuat kunci enkripsi...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Lanjutkan", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Perangkat tidak aman", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Maaf, kami tidak dapat membuat kunci yang aman pada perangkat ini.\n\nHarap mendaftar dengan perangkat lain.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Kunci pemulihan disalin ke papan klip", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Kunci pemulihan", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Jika Anda lupa kata sandi, satu-satunya cara memulihkan data Anda adalah dengan kunci ini.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Kami tidak menyimpan kunci ini, jadi harap simpan kunci yang berisi 24 kata ini dengan aman.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Lakukan lain kali", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Simpan kunci", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Kunci pemulihan sudah tersimpan di folder 'Downloads'!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Tidak punya kunci pemulihan?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Autentikasi dua langkah", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Masukkan kode 6 digit dari aplikasi autentikator Anda", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Perangkat hilang?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Masukkan kunci pemulihan Anda", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Pulihkan", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Mengeluarkan akun...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Segera", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Kunci aplikasi", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Kunci otomatis", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Tidak ditemukan kunci sistem", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Pasang kunci sandi atau kunci layar pada pengaturan sistem untuk menyalakan Pengunci Gawai.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Pilih layar kunci bawaan gawai Anda ATAU layar kunci kustom dengan PIN atau kata sandi.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Kunci perangkat", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Durasi waktu aplikasi akan terkunci setelah aplikasi ditutup", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Sembunyikan isi", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Menyembunyikan konten aplikasi di pemilih aplikasi dan menonaktifkan tangkapan layar", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Menyembunyikan konten aplikasi di pemilih aplikasi", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Terlalu banyak percobaan yang salah", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Ketuk untuk membuka", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Anda yakin ingin keluar dari akun ini?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ya, keluar akun", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Harap lakukan autentikasi untuk melihat rahasia Anda", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Selanjutnya", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Pasang kata sandi baru", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Masukkan PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Pasang PIN yang baru", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Konfirmasikan", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Masukkan kembali kata sandi", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Masukkan kembali PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verifikasikan identitas Anda", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Tidak dikenal. Coba lagi.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Sukses", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Batalkan", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Autentikasi diperlukan", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometrik diperlukan", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Kredensial perangkat diperlukan", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Kredensial perangkat diperlukan", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Pergi ke pengaturan", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Tidak ada autentikasi biometrik pada gawai Anda. Buka 'Pengaturan > Keamanan' untuk menambahkan autentikasi biometrik pada gawai Anda.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Autentikasi biometrik dimatikan. Kunci dan buka layar Anda untuk menyalakan autentikasi biometrik.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Oke", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Email sudah terdaftar.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Email belum terdaftar.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Surel ini sudah dipakai!", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Surel sudah diganti menjadi {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Gagal mengautentikasi. Mohon coba lagi", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Sukses mengautentikasi!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sesi berakhir", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Kunci pemulihan takbenar", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Kunci pemulihan yang Anda masukkan takbenar", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Autentikasi dwifaktor sukses diatur ulang", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Kode verifikasi Anda telah kedaluwarsa", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Kode takbenar", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Maaf, kode yang Anda masukkan takbenar", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_it.arb b/mobile/packages/strings/lib/l10n/arb/strings_it.arb new file mode 100644 index 0000000000..55a82de794 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_it.arb @@ -0,0 +1,704 @@ +{ + "networkHostLookUpErr": "Impossibile connettersi a Ente, controlla le impostazioni di rete e contatta l'assistenza se l'errore persiste.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Impossibile connettersi a Ente, riprova tra un po' di tempo. Se l'errore persiste, contatta l'assistenza.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Sembra che qualcosa sia andato storto. Riprova tra un po'. Se l'errore persiste, contatta il nostro team di supporto.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Errore", + "@error": { + "description": "Generic error title" + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contatta il supporto", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Invia una mail dei tuoi log", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Invia i log a \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copia indirizzo email", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Esporta log", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Annulla", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Segnala un problema", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Connesso a {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Salva", + "@save": { + "description": "Label for save button" + }, + "send": "Invia", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Vuoi salvarlo nel tuo spazio di archiviazione (cartella Download per impostazione predefinita) o inviarlo ad altre applicazioni?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Vuoi salvarlo nel tuo spazio di archiviazione (cartella Download per impostazione predefinita)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Inserisci il tuo nuovo indirizzo email", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Verifica", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Indirizzo email non valido", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Inserisci un indirizzo email valido.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Attendere prego...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verifica la password", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Password sbagliata", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Per favore riprova", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Inserisci la password", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Inserisci la tua password", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sessioni attive", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Oops", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Qualcosa è andato storto, per favore riprova", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Questo ti disconnetterà da questo dispositivo!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Questo ti disconnetterà dal seguente dispositivo:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Termina sessione?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Termina", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Questo dispositivo", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Crea account", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Debole", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Mediocre", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Forte", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Elimina account", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Ci dispiace vederti andare via. Stai avendo qualche problema?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Sì, invia un feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "No, elimina l'account", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Si prega di autenticarsi per avviare l'eliminazione dell'account", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Conferma l'eliminazione dell'account", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Questo account è collegato ad altre app di Ente, se ne utilizzi.\n\nI tuoi dati caricati, su tutte le app di Ente, saranno pianificati per la cancellazione e il tuo account verrà eliminato definitivamente.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Cancella", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Crea un nuovo account", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Password", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Conferma la password", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Forza password: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Dove hai sentito parlare di Ente? (opzionale)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Non teniamo traccia delle installazioni dell'app. Sarebbe utile se ci dicessi dove ci hai trovato!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Accetto i termini di servizio e la politica sulla privacy", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Termini", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Politica sulla Privacy", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Comprendo che se perdo la password, potrei perdere l'accesso ai miei dati poiché i miei dati sono criptati end-to-end.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Crittografia", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Accedi", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Bentornato!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Cliccando sul pulsante Accedi, accetto i termini di servizio e la politica sulla privacy", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Nessuna connessione internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Si prega di verificare la propria connessione Internet e riprovare.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verifica fallita, per favore riprova", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Ricrea password", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Il dispositivo attuale non è abbastanza potente per verificare la tua password, ma la possiamo rigenerare in un modo che funzioni su tutti i dispositivi.\n\nEffettua il login utilizzando la tua chiave di recupero e rigenera la tua password (puoi utilizzare nuovamente la stessa se vuoi).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Utilizza un codice di recupero", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Password dimenticata", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Modifica email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verifica email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Abbiamo inviato una mail a {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Per reimpostare la tua password, prima verifica la tua email.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Per favore, controlla la tua casella di posta (e lo spam) per completare la verifica", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tocca per inserire il codice", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Invia email", + "resendEmail": "Rinvia email", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "La verifica è ancora in corso", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sessione scaduta", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "La sessione è scaduta. Si prega di accedere nuovamente.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verifica della passkey", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "In attesa di verifica...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Riprova", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Verifica stato", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Login con TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Recupera account", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Imposta password", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Modifica password", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Reimposta password", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Chiavi di crittografia", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Inserisci una password per criptare i tuoi dati", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Inserisci una nuova password per criptare i tuoi dati", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Non memorizziamo questa password, quindi se te la dimentichi, non possiamo decriptare i tuoi dati", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Come funziona", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generazione delle chiavi di crittografia...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Password modificata con successo", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Esci dagli altri dispositivi", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Se pensi che qualcuno possa conoscere la tua password, puoi forzare tutti gli altri dispositivi che usano il tuo account ad uscire.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Esci dagli altri dispositivi", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Non uscire", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generazione delle chiavi di crittografia...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continua", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Dispositivo non sicuro", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Siamo spiacenti, non possiamo generare le chiavi sicure su questo dispositivo.\n\nPer favore, accedi da un altro dispositivo.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Chiave di recupero copiata negli appunti", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Chiave di recupero", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Se dimentichi la password, l'unico modo per recuperare i tuoi dati è con questa chiave.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Non memorizziamo questa chiave, per favore salva questa chiave di 24 parole in un posto sicuro.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Fallo più tardi", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Salva chiave", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Chiave di recupero salvata nella cartella Download!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Nessuna chiave di recupero?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Autenticazione a due fattori", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Inserisci il codice di 6 cifre dalla tua app di autenticazione", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Dispositivo perso?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Inserisci la tua chiave di recupero", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recupera", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Disconnessione...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Immediatamente", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Blocco app", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Blocco automatico", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nessun blocco di sistema trovato", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Per attivare il blocco del dispositivo, impostare il codice di accesso o il blocco dello schermo nelle impostazioni del sistema.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Scegli tra la schermata di blocco predefinita del dispositivo e una schermata di blocco personalizzata con PIN o password.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Blocco del dispositivo", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Blocco con PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Tempo dopo il quale l'applicazione si blocca dopo essere stata messa in background", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Nascondi il contenuto", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Nasconde il contenuto nel selettore delle app e disabilita gli screenshot", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Nasconde il contenuto nel selettore delle app", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Troppi tentativi errati", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tocca per sbloccare", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Sei sicuro di volerti disconnettere?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Si, effettua la disconnessione", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Autenticati per visualizzare i tuoi segreti", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Successivo", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Imposta una nuova password", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Inserisci PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Imposta un nuovo PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Conferma", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Reinserisci la password", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Reinserisci il PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verifica l'identità", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Non riconosciuto. Riprova.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Successo", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Annulla", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Autenticazione necessaria", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Autenticazione biometrica richiesta", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Credenziali del dispositivo richieste", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Credenziali del dispositivo richieste", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Vai alle impostazioni", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "L'autenticazione biometrica non è impostata sul tuo dispositivo. Vai a 'Impostazioni > Sicurezza' per impostarla.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "L'autenticazione biometrica è disabilitata. Blocca e sblocca lo schermo per abilitarla.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Email già registrata.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Email non registrata.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Questa email é già in uso", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Email modificata in {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Autenticazione non riuscita, riprova", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Autenticazione riuscita!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sessione scaduta", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Chiave di recupero errata", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "La chiave di recupero che hai inserito non è corretta", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Autenticazione a due fattori ripristinata con successo", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Il tuo codice di verifica è scaduto", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Codice errato", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Spiacenti, il codice che hai inserito non è corretto", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ja.arb b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb index 70fff21fe5..538533fa1d 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ja.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb @@ -1,9 +1,700 @@ { "networkHostLookUpErr": "Enteに接続できませんでした。ネットワーク設定を確認し、エラーが解決しない場合はサポートにお問い合わせください。", - "networkConnectionRefusedErr": "Enteに接続できません。しばらくしてから再試行してください。エラーが継続する場合は、サポートにお問い合わせください。", - "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "問題が発生したようです。しばらくしてから再試行してください。エラーが継続する場合は、サポートチームにお問い合わせください。", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Enteに接続できませんでした。しばらくしてから再試行してください。エラーが解決しない場合は、サポートにお問い合わせください。", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "問題が発生したようです。しばらくしてから再試行してください。エラーが解決しない場合は、サポートチームにお問い合わせください。", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, "error": "エラー", + "@error": { + "description": "Generic error title" + }, "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, "faq": "FAQ", - "contactSupport": "サポートに連絡する" -} + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "サポートに問い合わせ", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "ログをメールで送信", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "ログを以下のアドレスに送信してください \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "メールアドレスをコピー", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "ログのエクスポート", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "キャンセル", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "バグを報告", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "{endpoint} に接続しました", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "保存", + "@save": { + "description": "Label for save button" + }, + "send": "送信", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "これをストレージ (デフォルトではダウンロードフォルダ) に保存しますか、もしくは他のアプリに送信しますか?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "これをストレージに保存しますか? (デフォルトではダウンロードフォルダに保存)", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "E メール", + "@email": { + "description": "Email field label" + }, + "verify": "認証", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "メールアドレスが無効です", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "有効なメールアドレスを入力して下さい", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "お待ちください...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "パスワードを確認", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "パスワードが正しくありません", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "再度お試しください", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "パスワードを入力", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "パスワードを入力してください", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "アクティブセッション", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "おっと", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "問題が発生しました、再試行してください", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "このデバイスからログアウトします!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "以下のデバイスからログアウトします:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "セッションを終了しますか?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "終了", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "このデバイス", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "アカウント作成", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "脆弱", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "まあまあ", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "強力", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "アカウント削除", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "ご不便をおかけして申し訳ありません。なにか問題が発生していますか?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "はい、フィードバックを送信します", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "いいえ、アカウントを削除します", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "アカウントの削除を開始するためには認証が必要です", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "アカウントの削除に同意", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "このアカウントは他のEnteアプリも使用している場合はそれらにも紐づけされています。\nすべてのEnteアプリでアップロードされたデータは削除され、アカウントは完全に削除されます。", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "削除", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "新規アカウント作成", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "パスワード", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "パスワードの確認", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "パスワードの強度: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Ente についてどのようにお聞きになりましたか?(任意)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "利用規約プライバシー ポリシーに同意します", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "利用規約", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "プライバシーポリシー", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "私のデータはエンドツーエンドで暗号化されるため、パスワードを紛失した場合、データが失われる可能性があることを理解しています。", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "暗号化", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "ログイン", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "おかえりなさい!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "ログインをクリックする場合、利用規約およびプライバシー ポリシーに同意するものとします。", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "インターネット接続なし", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "インターネット接続を確認して、再試行してください。", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "確認に失敗しました、再試行してください", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "パスワードを再設定", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "現在のデバイスはパスワードを確認するのには不十分ですが、すべてのデバイスで動作するように再生成することはできます。\n\n回復キーを使用してログインし、パスワードを再生成してください(ご希望の場合は同じものを再度使用できます)。", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "回復キーを使用", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "パスワードを忘れた場合", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "メールアドレスを変更", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "メールアドレス認証", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "{email} にメールを送信しました", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "パスワードをリセットするには、メールの確認を先に行ってください。", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "受信トレイ(および迷惑メール)を確認して認証を完了してください", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "タップしてコードを入力", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "メール送信", + "resendEmail": "メールを再送信", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "認証はまだ保留中です", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "セッションの有効期限が切れました", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "セッションの有効期限が切れました。再度ログインしてください。", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "パスキー認証", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "認証を待っています...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "再試行", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "ステータスの確認", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "TOTPでログイン", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "アカウントを回復", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "パスワードの設定", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "パスワードの変更", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "パスワードのリセット", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "暗号鍵", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "データの暗号化に使用するパスワードを入力してください", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "データの暗号化に使用する新しいパスワードを入力してください", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "私たちはこのパスワードを保存していないので、あなたがそれを忘れた場合に私たちがあなたのデータを代わりに復号することはできません", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "動作の仕組み", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "暗号鍵を生成中...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "パスワードを変更しました", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "他のデバイスからサインアウトする", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "他の誰かがあなたのパスワードを知っている可能性があると判断した場合は、あなたのアカウントを使用している他のすべてのデバイスから強制的にサインアウトできます。", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "他のデバイスからサインアウトする", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "サインアウトしない", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "暗号化鍵を生成中...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "続行", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "安全ではないデバイス", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "申し訳ありませんが、このデバイスでは安全な鍵を生成できませんでした。\n\n別のデバイスから登録してください。", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "回復キーをクリップボードにコピーしました", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "回復キー", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "パスワードを忘れた場合、データを回復できる唯一の方法がこのキーです。", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "私たちはこのキーを保存しません。この 24 単語のキーを安全な場所に保存してください。", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "後で行う", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "キーを保存", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "リカバリキーをダウンロードフォルダに保存しました!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "回復キーがありませんか?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "2 要素認証", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "認証アプリに表示された 6 桁のコードを入力してください", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "デバイスを紛失しましたか?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "回復キーを入力", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "回復", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "ログアウト中...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "すぐに", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "アプリのロック", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "自動ロック", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "システムロックが見つかりませんでした", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "端末のロックを有効にするには、システム設定で端末のパスコードまたは画面ロックを設定してください。", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "端末のデフォルトのロック画面と、PINまたはパスワードを使用したカスタムロック画面を選択します。", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "生体認証", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "アプリをバックグラウンドでロックするまでの時間", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "内容を非表示", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "アプリの内容を非表示にし、スクリーンショットを無効にします", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "アプリを切り替えた際に、アプリの内容を非表示にします", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "間違った回数が多すぎます", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "タップして解除", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "本当にログアウトしてよろしいですか?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "はい、ログアウトします", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "秘密鍵を閲覧するためには認証が必要です", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "次へ", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "新しいパスワードを設定", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "PINを入力してください", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "新しいPINを設定", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "確認", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "パスワードを再入力してください", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "PINを再入力してください", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "本人を確認する", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "認識できません。再試行してください。", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "成功", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "キャンセル", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "認証が必要です", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "生体認証が必要です", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "デバイスの認証情報が必要です", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "デバイスの認証情報が必要です", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "設定を開く", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "生体認証がデバイスで設定されていません。生体認証を追加するには、\"設定 > セキュリティ\"を開いてください。", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "生体認証が無効化されています。画面をロック・ロック解除して生体認証を有効化してください。", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "メールアドレスはすでに登録されています。", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "メールアドレスはまだ登録されていません。", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "このアドレスは既に使用されています", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "メールアドレスが {newEmail} に変更されました", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "認証に失敗しました、再試行してください", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "認証に成功しました!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "セッションが失効しました", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "不正な回復キー", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "入力された回復キーは正しくありません", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "2 要素認証は正常にリセットされました", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "確認コードが失効しました", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "不正なコード", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "申し訳ありませんが、入力されたコードは正しくありません", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ka.arb b/mobile/packages/strings/lib/l10n/arb/strings_ka.arb new file mode 100644 index 0000000000..9ef5cf62f0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ka.arb @@ -0,0 +1,110 @@ +{ + "contactSupport": "მხარდაჭერის გუნდთან დაკავშირება", + "@contactSupport": { + "description": "Contact support button label" + }, + "cancel": "გაუქმება", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "პრობლემის შესახებ შეტყობინება", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "email": "ელექტრონული ფოსტა", + "@email": { + "description": "Email field label" + }, + "pleaseWait": "გთხოვთ, დაელოდოთ...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "პაროლის დასტური", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "არასწორი პაროლი", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "გთხოვთ, სცადოთ ხელახლა", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "delete": "წაშლა", + "@delete": { + "description": "Delete button label" + }, + "hearUsWhereTitle": "როგორ შეიტყვეთ Ente-ს შესახებ? (არასავალდებულო)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "ჩვენ არ ვაკონტროლებთ აპლიკაციის ინსტალაციას. სასარგებლო იქნებოდა, თუ გვეტყოდით, სად გვიპოვეთ!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "welcomeBack": "კეთილი იყოს თქვენი დაბრუნება!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "noInternetConnection": "ინტერნეტთან კავშირი არ არის", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "გთხოვთ, შეამოწმოთ თქვენი ინტერნეტ კავშირი და სცადოთ ხელახლა.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "changeEmail": "ელექტრონული ფოსტის შეცვლა", + "@changeEmail": { + "description": "Change email button label" + }, + "signOutFromOtherDevices": "ყველა მოწყობილობიდან გამოსვლა", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "თუ ფიქრობთ, რომ ვინმემ შესაძლოა იცოდეს თქვენი პაროლი, შეგიძლიათ ყველა მოწყობილობაზე იძულებითი გამოსვლა, რომელიც იყენებს თქვენს ანგარიშს.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "სხვა მოწყობილობებიდან გამოსვლა", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "არ მოხდეს გამოსვლა", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "მიმდინარეობს დაშიფრვის გასაღებების გენერირება...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "recoveryKey": "აღდგენის კოდი", + "@recoveryKey": { + "description": "Recovery key label" + }, + "loggingOut": "მიმდინარეობს გამოსვლა...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "androidBiometricNotRecognized": "ამოცნობა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "წარმატებით", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "გაუქმება", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "საჭიროა აუთენთიფიკაცია", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "sessionExpired": "სესიის დრო ამოიწურა", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_km.arb b/mobile/packages/strings/lib/l10n/arb/strings_km.arb new file mode 100644 index 0000000000..8d3de3eee4 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_km.arb @@ -0,0 +1,188 @@ +{ + "error": "មានកំហុស", + "@error": { + "description": "Generic error title" + }, + "ok": "យល់ព្រម", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "សំនួរ-ចម្លើយ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "ទំនាក់ទំនងសេវា", + "@contactSupport": { + "description": "Contact support button label" + }, + "copyEmailAddress": "ចម្លង​អាសយដ្ឋាន​អ៊ីមែល", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "នាំចេញកំណត់ហេតុ", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "បោះបង់", + "@cancel": { + "description": "Cancel button label" + }, + "save": "រក្សាទុក", + "@save": { + "description": "Label for save button" + }, + "send": "ផ្ញើរចេញ", + "@send": { + "description": "Label for send button" + }, + "email": "អ៊ីម៉ែល", + "@email": { + "description": "Email field label" + }, + "verify": "ផ្ទៀងផ្ទាត់", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "អ៊ីម៉ែលនេះមិនត្រឹមត្រូវទេ", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "verifyPassword": "ផ្ទៀងផ្ទាត់ពាក្យសម្ងាត់", + "@verifyPassword": { + "description": "Verify password button label" + }, + "pleaseTryAgain": "សូមសាកល្បងម្តងទៀត", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "វាយបញ្ចូល​ពាក្យ​សម្ងាត់", + "@enterPassword": { + "description": "Enter password field label" + }, + "activeSessions": "សកម្មភាពគណនី", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "somethingWentWrongPleaseTryAgain": "មាន​អ្វីមួយ​មិន​ប្រក្រតី។ សូម​ព្យាយាម​ម្តង​ទៀត", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "terminateSession": "កាត់់ផ្តាច់សកម្មភាពគណនីនេះ?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "កាត់ផ្តាច់", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "ទូរស័ព្ទនេះ", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "បង្កើតគណនី", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "ខ្សោយ", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "មធ្យម", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "ខ្លាំង", + "@strongStrength": { + "description": "Strong password strength label" + }, + "createNewAccount": "បង្កើតគណនីថ្មី", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "ពាក្យសម្ងាត់", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "បញ្ជាក់ពាក្យសម្ងាត់", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "កម្លាំងពាក្យសម្ងាត់: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "privacyPolicyTitle": "គោលការណ៍​ភាព​ឯកជន", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "logInLabel": "ចូលគណនី", + "@logInLabel": { + "description": "Log in button label" + }, + "forgotPassword": "ភ្លេចកូដសម្ងាត់", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "resendEmail": "ផ្ញើអ៊ីមែលម្ដងទៀត", + "@resendEmail": { + "description": "Resend email button label" + }, + "checkStatus": "ពិនិត្យស្ថានភាព", + "@checkStatus": { + "description": "Check status button label" + }, + "resetPasswordTitle": "ផ្ដាស់ប្ដូរពាក្សសម្ងាត់", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "recover": "សង្គ្រោះមកវិញ", + "@recover": { + "description": "Recover button label" + }, + "immediately": "ភ្លាមៗ", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "hideContent": "លាក់ពត័មាន", + "@hideContent": { + "description": "Hide content setting title" + }, + "tapToUnlock": "ប៉ះដើម្បីដោះសោ", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "enterPin": "វាយបញ្ចូល​លេខ​កូដ PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "reEnterPassword": "បញ្ចូលពាក្យសម្ងាត់ម្តងទៀត", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "androidCancelButton": "បោះបង់", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "តម្រូវឱ្យមានការបញ្ជាក់ភាពត្រឹមត្រូវ", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "emailChangedTo": "អ៊ីម៉ែលត្រូវបានផ្លាស់ប្តូរទៅ{newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ko.arb b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb index f71b8196d3..afc2095077 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ko.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb @@ -1,3 +1,700 @@ { - "networkHostLookUpErr": "Ente에 접속할 수 없습니다, 네트워크 설정을 확인해주시고 에러가 반복되는 경우 저희 지원 팀에 문의해주세요." -} + "networkHostLookUpErr": "Ente에 접속할 수 없습니다, 네트워크 설정을 확인해주시고 에러가 반복되는 경우 저희 지원 팀에 문의해주세요.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Ente에 접속할 수 없습니다, 잠시 후에 다시 시도해주세요. 에러가 반복되는 경우, 저희 지원 팀에 문의해주세요.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "뭔가 잘못된 것 같습니다. 잠시 후에 다시 시도해주세요. 에러가 반복되는 경우, 저희 지원 팀에 문의해주세요.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "에러", + "@error": { + "description": "Generic error title" + }, + "ok": "확인", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "지원 문의", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "로그를 이메일로 보내기", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "이 로그를 {toEmail} 쪽으로 보내주세요", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "이메일 주소 복사", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "로그 내보내기", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "취소", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "버그 제보", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "{endpoint}에 접속됨", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "저장", + "@save": { + "description": "Label for save button" + }, + "send": "보내기", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "이것을 당신의 스토리지 (일반적으로 다운로드 폴더) 에 저장하시겠습니까, 아니면 다른 App으로 전송하시겠습니까?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "이것을 당신의 스토리지 (일반적으로 다운로드 폴더) 에 저장하시겠습니까?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "이메일", + "@email": { + "description": "Email field label" + }, + "verify": "인증", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "잘못 된 이메일 주소", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "유효한 이메일 주소를 입력해주세요", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "잠시만 기다려주세요...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "비밀번호 확인", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "올바르지 않은 비밀번호", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "다시 시도해주세요", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "암호 입력", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "암호 입력", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "활성화된 Session", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "이런!", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "뭔가 잘못됐습니다, 다시 시도해주세요", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "이 작업을 하시면 기기에서 로그아웃하게 됩니다!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "이 작업을 하시면 다음 기기에서 로그아웃하게 됩니다:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "세션을 종결하시겠습니까?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "종결", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "이 기기", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "계정 만들기", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "약함", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "보통", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "강함", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "계정 삭제하기", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "떠나신다니 아쉽습니다. 뭔가 문제가 있으셨나요?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "네, 피드백을 보냅니다", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "아니오, 계정을 지웁니다", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "계정 삭제 절차를 시작하려면 인증 절차를 거쳐주세요", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "계정 삭제 확인", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "다른 Ente의 서비스를 이용하고 계시다면, 해당 계정은 모두 연결이 되어있습니다.\n\n모든 Ente 서비스에 업로드 된 당신의 데이터는 삭제 수순에 들어가며, 계정은 불가역적으로 삭제됩니다.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "삭제", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "새 계정 만들기", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "암호", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "암호 확인", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "암호 보안 강도: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Ente에 대해 어떻게 알게 되셨나요? (선택사항)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "저희는 어플 설치 과정을 관찰하지 않습니다. 어디에서 저희를 발견하셨는지 알려주신다면 도움이 될 겁니다!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "나는 사용자 약관개인정보 취급방침에 동의합니다.", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "약관", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "개인정보 취급 방침", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "나는 암호를 분실한 경우, 데이터가 종단 간 암호화되어있기에 데이터를 손실할 수 있음을 이해합니다.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "암호화", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "로그인", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "돌아오신 것을 환영합니다!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "로그인을 누름으로써, 나는 사용자 약관개인정보 취급방침에 동의합니다.", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "인터넷 연결 없음", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "인터넷 연결을 확인하시고 다시 시도해주세요.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "검증 실패, 다시 시도해주세요", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "암호 다시 생성", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "현재 사용 중인 기기는 암호를 확인하기에 적합하지 않으나, 모든 기기에서 작동하는 방식으로 비밀번호를 다시 생성할 수 있습니다.\n\n복구 키를 사용하여 로그인하고 암호를 다시 생성해주세요. (원하시면 현재 사용 중인 암호와 같은 암호를 다시 사용하실 수 있습니다.)", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "복구 키 사용", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "암호 분실", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "이메일 변경", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "이메일 인증하기", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "{email} 쪽으로 메일을 보냈습니다", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "암호를 재설정하시려면, 먼저 이메일을 인증해주세요.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "검증을 위해 메일 보관함을 (또는 스팸 메일 보관함) 확인해주세요", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "눌러서 코드 입력하기", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "이메일 보내기", + "resendEmail": "이메일 다시 보내기", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "검증 절차가 마무리되지 않았습니다", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "세션 만료됨", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "세션이 만료되었습니다. 다시 로그인해주세요.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "패스키 검증", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "검증 대기 중...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "다시 시도해주세요", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "상태 확인", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "TOTP로 로그인 하기", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "계정 복구", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "암호 지정", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "암호 변경", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "암호 초기화", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "암호화 키", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "데이터 암호화를 위한 암호 입력", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "데이터 암호화를 위한 새로운 암호 입력", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "저희는 이 암호를 저장하지 않으니, 만약 잊어버리시게 되면, 데이터를 복호화 해드릴 수 없습니다", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "작동 원리", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "암호화 키 생성 중...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "암호가 성공적으로 변경되었습니다", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "다른 기기들에서 로그아웃하기", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "다른 사람이 내 암호를 알 수도 있을 거란 의심이 드신다면, 당신의 계정을 사용 중인 다른 모든 기기에서 로그아웃할 수 있습니다.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "다른 기기들을 로그아웃시키기", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "로그아웃 하지 않기", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "암호화 키를 생성하는 중...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "계속", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "보안이 허술한 기기", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "죄송합니다, 이 기기에서 보안 키를 생성할 수 없습니다.\n\n다른 기기에서 계정을 생성해주세요.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "클립보드에 복구 키 복사 됨", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "복구 키", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "암호를 잊어버린 경우, 데이터를 복구하려면 이 키를 이용하는 방법 뿐입니다.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "저희는 이 키를 보관하지 않으니, 여기에 있는 24 단어로 구성된 키를 안전하게 보관해주세요.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "나중에 하기", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "키 저장하기", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "다운로드 폴더에 복구 키가 저장되었습니다!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "복구 키가 없으세요?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "2단계 인증", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Authenticator에 적힌 6 자리 코드를 입력해주세요", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "기기를 잃어버리셨나요?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "복구 키를 입력하세요", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "복구", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "로그아웃하는 중...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "즉시", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "어플 잠금", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "자동 잠금", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "시스템 잠금 찾을 수 없음", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "기기 잠금을 활성화하시려면, 기기의 암호를 만들거나 시스템 설정에서 화면 잠금을 설정해주세요.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "기본 잠금 화면이나, PIN 번호나 암호를 사용한 사용자 설정 잠금 화면 중에 선택하세요.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "기기 잠금", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin 잠금", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Background로 App 넘어가고 잠기기까지 걸리는 시간", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "내용 숨기기", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "App 전환 화면에서 App의 내용을 숨기고 Screenshot 촬영을 막습니다", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "App 전환 화면에서 App의 내용을 숨깁니다", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "잘못된 시도 횟수가 너무 많습니다", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "잠금을 해제하려면 누르세요", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "로그아웃 하시겠습니까?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "네, 로그아웃하기", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "비밀 부분을 확인하려면 인증 절차를 거쳐주세요", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "다음", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "새 비밀번호 설정", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "PIN 번호 입력", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "새 PIN 번호 설정", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "확인", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "암호 다시 입력", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "핀 다시 입력", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "신원 확인", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "식별할 수 없습니다. 다시 시도해주세요.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "성공", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "취소", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "인증 필요", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "생체인증 필요", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "장치 자격 증명 필요", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "장치 자격 증명 필요", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "설정으로 가기", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "기기에 생체인증이 설정되어있지 않습니다. '설정 > 보안'으로 가셔서 생체인증을 설정해주세요.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "생체인증에 문제가 있습니다. 활성화하시려면 기기를 잠궜다가 다시 풀어주세요.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "확인", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "이미 등록된 이메일입니다.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "등록되지 않은 이메일입니다.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "이 이메일은 이미 사용 중입니다", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "{newEmail}로 메일이 변경되었습니다", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "인증절차 실패, 다시 시도해주세요", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "인증 성공!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "세션 만료", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "잘못 된 복구 키", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "입력하신 복구 키가 맞지 않습니다", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "2FA가 성공적으로 초기화되었습니다", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "검증 코드의 유효시간이 경과하였습니다", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "잘못된 코드", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "죄송합니다, 입력하신 코드가 맞지 않습니다", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_lt.arb b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb index eca6c75216..97121f3a3e 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_lt.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb @@ -1,3 +1,700 @@ { - "networkHostLookUpErr": "Nepavyksta prisijungti prie \"Ente\". Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi." -} + "networkHostLookUpErr": "Nepavyksta prisijungti prie „Ente“. Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Nepavyksta prisijungti prie „Ente“. Bandykite dar kartą po kurio laiko. Jei klaida tęsiasi, susisiekite su palaikymo komanda.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Atrodo, kad kažkas nutiko ne taip. Bandykite dar kartą po kurio laiko. Jei klaida tęsiasi, susisiekite su mūsų palaikymo komanda.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Klaida", + "@error": { + "description": "Generic error title" + }, + "ok": "Gerai", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "DUK", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Susisiekti su palaikymo komanda", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Atsiųskite žurnalus el. laišku", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Siųskite žurnalus adresu\n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopijuoti el. pašto adresą", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Eksportuoti žurnalus", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Atšaukti", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Pranešti apie riktą", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Prijungta prie {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Išsaugoti", + "@save": { + "description": "Label for save button" + }, + "send": "Siųsti", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Ar norite tai išsaugoti saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke), ar siųsti į kitas programas?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Ar norite tai išsaugoti savo saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "El. paštas", + "@email": { + "description": "Email field label" + }, + "verify": "Patvirtinti", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Netinkamas el. pašto adresas", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Įveskite tinkamą el. pašto adresą.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Palaukite...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Patvirtinkite slaptažodį", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Neteisingas slaptažodis.", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Bandykite dar kartą.", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Įveskite slaptažodį", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Įveskite savo slaptažodį", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktyvūs seansai", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Kažkas nutiko ne taip. Bandykite dar kartą.", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Tai jus atjungs nuo šio įrenginio.", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Tai jus atjungs nuo toliau nurodyto įrenginio:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Baigti seansą?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Baigti", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Šis įrenginys", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Kurti paskyrą", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Silpna", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Vidutinė", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Stipri", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Ištrinti paskyrą", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Apgailestausime, kad išeinate. Ar susiduriate su kažkokiomis problemomis?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Taip, siųsti atsiliepimą", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Ne, ištrinti paskyrą", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Nustatykite tapatybę, kad pradėtumėte paskyros ištrynimą", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Patvirtinkite paskyros ištrynimą", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Ši paskyra susieta su kitomis „Ente“ programomis, jei jas naudojate.\n\nJūsų įkelti duomenys per visas „Ente“ programas bus planuojama ištrinti, o jūsų paskyra bus ištrinta negrįžtamai.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Ištrinti", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Kurti naują paskyrą", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Slaptažodis", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Patvirtinkite slaptažodį", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Slaptažodžio stiprumas: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Kaip išgirdote apie „Ente“? (nebūtina)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote.", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Sutinku su paslaugų sąlygomis ir privatumo politika", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Sąlygos", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privatumo politika", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Suprantu, kad jei prarasiu slaptažodį, galiu prarasti savo duomenis, kadangi duomenys yra visapusiškai užšifruota.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Šifravimas", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Prisijungti", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Sveiki sugrįžę!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Spustelėjus Prisijungti sutinku su paslaugų sąlygomis ir privatumo politika", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Nėra interneto ryšio", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Patikrinkite savo interneto ryšį ir bandykite dar kartą.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Patvirtinimas nepavyko. Bandykite dar kartą.", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Iš naujo sukurti slaptažodį", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Dabartinis įrenginys nėra pakankamai galingas, kad patvirtintų jūsų slaptažodį, bet mes galime iš naujo sugeneruoti taip, kad jis veiktų su visais įrenginiais.\n\nPrisijunkite naudodami atkūrimo raktą ir sugeneruokite iš naujo slaptažodį (jei norite, galite vėl naudoti tą patį).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Naudoti atkūrimo raktą", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Pamiršau slaptažodį", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Keisti el. paštą", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Patvirtinti el. paštą", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Išsiuntėme laišką adresu {email}.", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Kad iš naujo nustatytumėte slaptažodį, pirmiausia patvirtinkite savo el. paštą.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Patikrinkite savo gautieją (ir šlamštą), kad užbaigtumėte patvirtinimą.", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Palieskite, kad įvestumėte kodą", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Siųsti el. laišką", + "resendEmail": "Iš naujo siųsti el. laišką", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Vis dar laukiama patvirtinimo", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Seansas baigėsi", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Jūsų seansas baigėsi. Prisijunkite iš naujo.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Slaptarakčio patvirtinimas", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Laukiama patvirtinimo...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Bandyti dar kartą", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Tikrinti būseną", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Prisijungti su TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Atkurti paskyrą", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Nustatyti slaptažodį", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Keisti slaptažodį", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Nustatyti slaptažodį iš naujo", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Šifravimo raktai", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Įveskite slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Įveskite naują slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Šio slaptažodžio nesaugome, todėl jei jį pamiršite, negalėsime iššifruoti jūsų duomenų", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Kaip tai veikia", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generuojami šifravimo raktai...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Slaptažodis sėkmingai pakeistas", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Atsijungti iš kitų įrenginių", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Jei manote, kad kas nors gali žinoti jūsų slaptažodį, galite priverstinai atsijungti iš visų kitų įrenginių, naudojančių jūsų paskyrą.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Atsijungti kitus įrenginius", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Neatsijungti", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generuojami šifravimo raktai...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Tęsti", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Nesaugus įrenginys", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Atsiprašome, šiame įrenginyje nepavyko sugeneruoti saugių raktų.\n\nRegistruokitės iš kito įrenginio.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Nukopijuotas atkūrimo raktas į iškarpinę", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Atkūrimo raktas", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Jei pamiršote slaptažodį, vienintelis būdas atkurti duomenis – naudoti šį raktą.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Šio rakto nesaugome, todėl išsaugokite šį 24 žodžių raktą saugioje vietoje.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Daryti tai vėliau", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Išsaugoti raktą", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Atkūrimo raktas išsaugotas atsisiuntimų aplanke.", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Neturite atkūrimo rakto?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Dvigubas tapatybės nustatymas", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Įveskite 6 skaitmenų kodą\niš autentifikatoriaus programos", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Prarastas įrenginys?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Įveskite atkūrimo raktą", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Atkurti", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Atsijungiama...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Iš karto", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Programos užraktas", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatinis užraktas", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nerastas sistemos užraktas", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Kad įjungtumėte įrenginio užraktą, sistemos nustatymuose nustatykite įrenginio prieigos kodą arba ekrano užraktą.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Pasirinkite tarp numatytojo įrenginio užrakinimo ekrano ir pasirinktinio užrakinimo ekrano su PIN kodu arba slaptažodžiu.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Įrenginio užraktas", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN užraktas", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Laikas, po kurio programa užrakinama perkėlus ją į foną.", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Slėpti turinį", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Paslepia programų turinį programų perjungiklyje ir išjungia ekrano kopijas.", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Paslepia programos turinį programos perjungiklyje.", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Per daug neteisingų bandymų.", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Palieskite, kad atrakintumėte", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Ar tikrai norite atsijungti?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Taip, atsijungti", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Nustatykite tapatybę, kad peržiūrėtumėte savo paslaptis", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Toliau", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Nustatykite naują slaptažodį", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Įveskite PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Nustatykite naują PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Patvirtinti", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Įveskite slaptažodį iš naujo", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Įveskite PIN iš naujo", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Patvirtinkite tapatybę", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Neatpažinta. Bandykite dar kartą.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Sėkmė", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Atšaukti", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Privalomas tapatybės nustatymas", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Privaloma biometrija", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Privalomi įrenginio kredencialai", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Privalomi įrenginio kredencialai", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Eiti į nustatymus", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometrinis tapatybės nustatymas jūsų įrenginyje nenustatytas. Eikite į Nustatymai > Saugumas ir pridėkite biometrinį tapatybės nustatymą.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrinis tapatybės nustatymas išjungtas. Kad jį įjungtumėte, užrakinkite ir atrakinkite ekraną.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Gerai", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "El. paštas jau užregistruotas.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "El. paštas neregistruotas.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Šis el. paštas jau naudojamas.", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "El. paštas pakeistas į {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Tapatybės nustatymas nepavyko. Bandykite dar kartą.", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Tapatybės nustatymas sėkmingas.", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Seansas baigėsi", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Neteisingas atkūrimo raktas", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Įvestas atkūrimo raktas yra neteisingas.", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Dvigubas tapatybės nustatymas sėkmingai iš naujo nustatytas.", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Jūsų patvirtinimo kodas nebegaliojantis.", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Neteisingas kodas", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Atsiprašome, įvestas kodas yra neteisingas.", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_lv.arb b/mobile/packages/strings/lib/l10n/arb/strings_lv.arb new file mode 100644 index 0000000000..4e94c83c00 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_lv.arb @@ -0,0 +1,125 @@ +{ + "error": "Kļūda", + "@error": { + "description": "Generic error title" + }, + "ok": "Labi", + "@ok": { + "description": "Generic OK button label" + }, + "cancel": "Atcelt", + "@cancel": { + "description": "Cancel button label" + }, + "save": "Saglabāt", + "@save": { + "description": "Label for save button" + }, + "email": "E-pasts", + "@email": { + "description": "Email field label" + }, + "invalidEmailTitle": "Nederīga e-pasta adrese", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Lūdzu ievadiet derīgu e-pasta adresi.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "enterPassword": "Ievadiet paroli", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Ievadiet savu paroli", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "thisDevice": "Šī ierīce", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Izveidot kontu", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Vāja", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Vidēji spēcīga", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Spēcīga", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Dzēst kontu", + "@deleteAccount": { + "description": "Delete account button label" + }, + "noDeleteAccountAction": "Nē, dzēst kontu", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "confirmAccountDeleteTitle": "Apstiprināt konta dzēšanu", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "delete": "Dzēst", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Izveidot jaunu kontu", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Parole", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Apstiprināt paroli", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "encryption": "Šifrēšana", + "@encryption": { + "description": "Encryption label" + }, + "noInternetConnection": "Nav interneta savienojums", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "howItWorks": "Kā tas darbojas", + "@howItWorks": { + "description": "How it works button label" + }, + "saveKey": "Saglabāt atslēgu", + "@saveKey": { + "description": "Save key button label" + }, + "hideContent": "Slēpt saturu", + "@hideContent": { + "description": "Hide content setting title" + }, + "androidCancelButton": "Atcelt", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Šis e-pasts jau tiek izmantots", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-pasts nomainīts uz {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ml.arb b/mobile/packages/strings/lib/l10n/arb/strings_ml.arb new file mode 100644 index 0000000000..29f3e3fffe --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ml.arb @@ -0,0 +1,62 @@ +{ + "ok": "ശരി", + "@ok": { + "description": "Generic OK button label" + }, + "cancel": "റദ്ദാക്കുക", + "@cancel": { + "description": "Cancel button label" + }, + "email": "ഇമെയിൽ", + "@email": { + "description": "Email field label" + }, + "verify": "പരിശോധിക്കുക", + "@verify": { + "description": "Verify button label" + }, + "verifyPassword": "പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "തെറ്റായ പാസ്‌വേഡ്", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "createNewAccount": "പുതിയ അക്കൗണ്ട് സൃഷ്ടിക്കുക", + "@createNewAccount": { + "description": "Create new account button label" + }, + "confirmPassword": "പാസ്വേഡ് സ്ഥിരീകരിക്കുക", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "welcomeBack": "വീണ്ടും സ്വാഗതം!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "changeEmail": "ഇമെയിൽ മാറ്റുക", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "ഇമെയിൽ സ്ഥിരീകരിക്കുക", + "@verifyEmail": { + "description": "Verify email title" + }, + "twoFactorAuthTitle": "ടു-ഫാക്ടർ ആധികാരികത", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "നിങ്ങളുടെ ഓതന്റിക്കേറ്റർ ആപ്പിൽ നിന്നുള്ള 6 അക്ക കോഡ് നൽകുക", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "emailAlreadyRegistered": "ഇമെയിൽ ഇതിനകം രജിസ്റ്റർ ചെയ്തിട്ടുണ്ട്.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "ഇമെയിൽ രജിസ്റ്റർ ചെയ്തിട്ടില്ല.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_nl.arb b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb index f3cc33b846..3ac69c6ce5 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_nl.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Kan geen verbinding maken met Ente, controleer uw netwerkinstellingen en neem contact op met ondersteuning als de fout zich blijft voordoen." -} + "networkHostLookUpErr": "Kan geen verbinding maken met Ente, controleer uw netwerkinstellingen en neem contact op met ondersteuning als de fout zich blijft voordoen.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Kan geen verbinding maken met Ente, probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Het lijkt erop dat er iets fout is gegaan. Probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met ons supportteam.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Foutmelding", + "@error": { + "description": "Generic error title" + }, + "ok": "Oké", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Veelgestelde vragen", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Klantenservice", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "E-mail uw logs", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Verstuur de logs alsjeblieft naar {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "E-mailadres kopiëren", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Logs exporteren", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Annuleer", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Een fout melden", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Verbonden met {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Opslaan", + "@save": { + "description": "Label for save button" + }, + "send": "Verzenden", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Wil je dit opslaan naar je opslagruimte (Downloads map) of naar andere apps versturen?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Wil je dit opslaan naar je opslagruimte (Downloads map)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Voer uw nieuwe e-mailadres in", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "verify": "Verifiëren", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Ongeldig e-mailadres", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Voer een geldig e-mailadres in.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Een ogenblik geduld...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Bevestig wachtwoord", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Onjuist wachtwoord", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Probeer het nog eens", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Voer wachtwoord in", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Voer je wachtwoord in", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Actieve sessies", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Oeps", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Er is iets fout gegaan, probeer het opnieuw", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Dit zal je uitloggen van dit apparaat!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Dit zal je uitloggen van het volgende apparaat:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Sessie beëindigen?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Beëindigen", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Dit apparaat", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Account aanmaken", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Zwak", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Matig", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Sterk", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Account verwijderen", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "We zullen het vervelend vinden om je te zien vertrekken. Zijn er problemen?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ja, geef feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nee, verwijder account", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Gelieve te verifiëren om het account te verwijderen", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Account verwijderen bevestigen", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Dit account is gekoppeld aan andere Ente apps, als je er gebruik van maakt.\n\nJe geüploade gegevens worden in alle Ente apps gepland voor verwijdering, en je account wordt permanent verwijderd voor alle Ente diensten.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Verwijderen", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Nieuw account aanmaken", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Wachtwoord", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Wachtwoord bevestigen", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Wachtwoord sterkte: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Hoe hoorde je over Ente? (optioneel)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Wij gebruiken geen tracking. Het zou helpen als je ons vertelt waar je ons gevonden hebt!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Voorwaarden", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privacybeleid", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Ik begrijp dat als ik mijn wachtwoord verlies, ik mijn gegevens kan verliezen omdat mijn gegevens end-to-end versleuteld zijn.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Encryptie", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Inloggen", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Welkom terug!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Door op inloggen te klikken, ga ik akkoord met de gebruiksvoorwaarden en privacybeleid", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Geen internetverbinding", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Controleer je internetverbinding en probeer het opnieuw.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verificatie mislukt, probeer het opnieuw", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Wachtwoord opnieuw instellen", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "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 herstelsleutel en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Herstelsleutel gebruiken", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Wachtwoord vergeten", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "E-mailadres wijzigen", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Bevestig e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "We hebben een e-mail gestuurd naar {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Verifieer eerst je e-mailadres om je wachtwoord opnieuw in te stellen.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Controleer je inbox (en spam) om verificatie te voltooien", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tik om code in te voeren", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "E-mail versturen", + "resendEmail": "E-mail opnieuw versturen", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Verificatie is nog in behandeling", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sessie verlopen", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Jouw sessie is verlopen. Log opnieuw in.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Passkey verificatie", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Wachten op verificatie...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Probeer opnieuw", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Status controleren", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Inloggen met TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Account herstellen", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Wachtwoord instellen", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Wachtwoord wijzigen", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Wachtwoord resetten", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Encryptiesleutels", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Voer een wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Voer een nieuw wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Wij slaan dit wachtwoord niet op, dus als je het vergeet, kunnen we jouw gegevens niet ontsleutelen", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Hoe het werkt", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Encryptiesleutels worden gegenereerd...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Wachtwoord succesvol aangepast", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Afmelden bij andere apparaten", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Als je denkt dat iemand je wachtwoord zou kunnen kennen, kun je alle andere apparaten die je account gebruiken dwingen om uit te loggen.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Afmelden bij andere apparaten", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Niet uitloggen", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Encryptiesleutels genereren...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Doorgaan", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Onveilig apparaat", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Sorry, we konden geen beveiligde sleutels genereren op dit apparaat.\n\nMeld je aan vanaf een ander apparaat.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Herstelsleutel gekopieerd naar klembord", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Herstelsleutel", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Als je je wachtwoord vergeet, kun je alleen met deze code je gegevens herstellen.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "We slaan deze code niet op, bewaar deze code met 24 woorden op een veilige plaats.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Doe dit later", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Sleutel opslaan", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Herstelsleutel opgeslagen in de Downloads map!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Geen herstelsleutel?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Tweestapsverificatie", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Voer de 6-cijferige code van je verificatie-app in", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Apparaat verloren?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Voer je herstelsleutel in", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Herstellen", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Bezig met uitloggen...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Onmiddellijk", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "App-vergrendeling", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatische vergrendeling", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Geen systeemvergrendeling gevonden", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Om toestelvergrendeling in te schakelen, stelt u de toegangscode van het apparaat of schermvergrendeling in uw systeeminstellingen in.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Kies tussen de standaard schermvergrendeling van uw apparaat en een aangepaste schermvergrendeling met een pincode of wachtwoord.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Apparaat vergrendeling", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin vergrendeling", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Tijd waarna de app vergrendelt nadat ze op de achtergrond is gezet", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Inhoud verbergen", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Verbergt de app inhoud in de app switcher en schakelt schermafbeeldingen uit", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Verbergt de inhoud van de app in de app switcher", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Te veel onjuiste pogingen", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tik om te ontgrendelen", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Weet je zeker dat je wilt uitloggen?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ja, uitloggen", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Graag verifiëren om uw herstelsleutel te bekijken", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Volgende", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Nieuw wachtwoord instellen", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Pin invoeren", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Nieuwe pin instellen", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Bevestig", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Wachtwoord opnieuw invoeren", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "PIN opnieuw invoeren", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Identiteit verifiëren", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Niet herkend. Probeer het opnieuw.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Succes", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Annuleren", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Verificatie vereist", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometrische verificatie vereist", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Apparaatgegevens vereist", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Apparaatgegevens vereist", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Ga naar instellingen", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometrische verificatie is niet ingesteld op uw apparaat. Ga naar 'Instellingen > Beveiliging' om biometrische verificatie toe te voegen.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrische verificatie is uitgeschakeld. Vergrendel en ontgrendel uw scherm om het in te schakelen.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Oké", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-mail is al geregistreerd.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-mail niet geregistreerd.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Dit e-mailadres is al in gebruik", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-mailadres gewijzigd naar {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Verificatie mislukt, probeer het opnieuw", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Verificatie geslaagd!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sessie verlopen", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Onjuiste herstelsleutel", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "De ingevoerde herstelsleutel is onjuist", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Tweestapsverificatie succesvol gereset", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Uw verificatiecode is verlopen", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Onjuiste code", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, de ingevoerde code is onjuist", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pl.arb b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb index bcd5668986..8f7c52e031 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_pl.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Nie można połączyć się z Ente, sprawdź ustawienia sieci i skontaktuj się z pomocą techniczną, jeśli błąd będzie się powtarzał." -} + "networkHostLookUpErr": "Nie można połączyć się z Ente, sprawdź ustawienia sieci i skontaktuj się z pomocą techniczną, jeśli błąd będzie się powtarzał.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Nie można połączyć się z Ente, spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z pomocą techniczną.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Wygląda na to, że coś poszło nie tak. Spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z naszym zespołem pomocy technicznej.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Błąd", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Najczęściej zadawane pytania (FAQ)", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Skontaktuj się z pomocą techniczną", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Wyślij mailem logi", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Prosimy wysłać logi do {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopiuj adres e-mail", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Eksportuj logi", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Anuluj", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Zgłoś błąd", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Połączono z {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Zapisz", + "@save": { + "description": "Label for save button" + }, + "send": "Wyślij", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane) czy wysłać to do innych aplikacji?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Wprowadź nowy adres e-mail", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Zweryfikuj", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Nieprawidłowy adres e-mail", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Prosimy podać prawidłowy adres e-mail.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Prosimy czekać...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Zweryfikuj hasło", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Nieprawidłowe hasło", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Prosimy spróbować ponownie", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Wprowadź hasło", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Wprowadź swoje hasło", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktywne sesje", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Coś poszło nie tak, spróbuj ponownie", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "To wyloguje Cię z tego urządzenia!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "To wyloguje Cię z tego urządzenia:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Zakończyć sesję?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Zakończ", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "To urządzenie", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Utwórz konto", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Słabe", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Umiarkowane", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Silne", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Usuń konto", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Będzie nam przykro, że odchodzisz. Masz jakiś problem?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Tak, wyślij opinię", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nie, usuń moje konto", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Prosimy uwierzytelnić się, aby zainicjować usuwanie konta", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Potwierdź usunięcie konta", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "To konto jest połączone z innymi aplikacjami Ente, jeśli ich używasz.\n\nTwoje przesłane dane, we wszystkich aplikacjach Ente, zostaną zaplanowane do usunięcia, a Twoje konto zostanie trwale usunięte.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Usuń", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Utwórz nowe konto", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Hasło", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Potwierdź hasło", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Siła hasła: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Jak usłyszałeś/aś o Ente? (opcjonalnie)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Akceptuję warunki korzystania z usługi i politykę prywatności", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Regulamin", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Polityka prywatności", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Rozumiem, że jeśli utracę hasło, mogę stracić moje dane, ponieważ moje dane są szyfrowane end-to-end.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Szyfrowanie", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Zaloguj się", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Witaj ponownie!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Klikając, zaloguj się, zgadzam się na regulamin i politykę prywatności", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Brak połączenia z Internetem", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Prosimy sprawdzić połączenie internetowe i spróbować ponownie.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Weryfikacja nie powiodła się, spróbuj ponownie", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Zresetuj hasło", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Obecne urządzenie nie jest wystarczająco wydajne, aby zweryfikować Twoje hasło, więc musimy je raz zregenerować w sposób, który działa ze wszystkimi urządzeniami. \n\nZaloguj się przy użyciu klucza odzyskiwania i zresetuj swoje hasło (możesz ponownie użyć tego samego, jeśli chcesz).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Użyj kodu odzyskiwania", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Nie pamiętam hasła", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Zmień adres e-mail", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Zweryfikuj adres e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Wysłaliśmy wiadomość do {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Aby zresetować hasło, najpierw zweryfikuj swój e-mail.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Dotknij, aby wprowadzić kod", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Wyślij e-mail", + "resendEmail": "Wyślij e-mail ponownie", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Weryfikacja jest nadal w toku", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sesja wygasła", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Twoja sesja wygasła. Zaloguj się ponownie.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Weryfikacja kluczem dostępu", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Oczekiwanie na weryfikację...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Spróbuj ponownie", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Sprawdź stan", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Zaloguj się za pomocą TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Odzyskaj konto", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Ustaw hasło", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Zmień hasło", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Zresetuj hasło", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Klucz szyfrowania", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Wprowadź hasło, którego możemy użyć do zaszyfrowania Twoich danych", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Wprowadź nowe hasło, którego możemy użyć do zaszyfrowania Twoich danych", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Nie przechowujemy tego hasła, więc jeśli go zapomnisz, nie będziemy w stanie odszyfrować Twoich danych", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Jak to działa", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generowanie kluczy szyfrujących...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Hasło zostało pomyślnie zmienione", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Wyloguj z pozostałych urządzeń", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Jeśli uważasz, że ktoś może znać Twoje hasło, możesz wymusić wylogowanie na wszystkich innych urządzeniach korzystających z Twojego konta.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Wyloguj z pozostałych urządzeń", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Nie wylogowuj mnie", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generowanie kluczy szyfrujących...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Kontynuuj", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Niezabezpieczone urządzenie", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Przepraszamy, nie mogliśmy wygenerować kluczy bezpiecznych na tym urządzeniu.\n\nZarejestruj się z innego urządzenia.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Klucz odzyskiwania został skopiowany do schowka", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Klucz odzyskiwania", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Jeśli zapomnisz hasła, jedynym sposobem na odzyskanie danych jest ten klucz.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Nie przechowujemy tego klucza, prosimy zachować ten 24-słowny klucz w bezpiecznym miejscu.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Zrób to później", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Zapisz klucz", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Klucz odzyskiwania zapisany w folderze Pobrane!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Brak klucza odzyskiwania?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Uwierzytelnianie dwustopniowe", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Wprowadź sześciocyfrowy kod z \nTwojej aplikacji uwierzytelniającej", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Zagubiono urządzenie?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Wprowadź swój klucz odzyskiwania", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Odzyskaj", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Wylogowywanie...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Natychmiast", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Blokada aplikacji", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatyczna blokada", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nie znaleziono blokady systemowej", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Aby włączyć blokadę aplikacji, należy skonfigurować hasło urządzenia lub blokadę ekranu w ustawieniach Twojego systemu.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Wybierz między domyślnym ekranem blokady urządzenia a niestandardowym ekranem blokady z kodem PIN lub hasłem.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Blokada urządzenia", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Blokada PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Czas, po którym aplikacja blokuje się po umieszczeniu jej w tle", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Ukryj zawartość", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Ukrywa zawartość aplikacji w przełączniku aplikacji i wyłącza zrzuty ekranu", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Ukrywa zawartość aplikacji w przełączniku aplikacji", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Zbyt wiele błędnych prób", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Naciśnij, aby odblokować", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Czy na pewno chcesz się wylogować?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Tak, wyloguj", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Prosimy uwierzytelnić się, aby wyświetlić swoje sekrety", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Dalej", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Ustaw nowe hasło", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Wprowadź kod PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Ustaw nowy kod PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Potwierdź", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Wprowadź ponownie hasło", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Wprowadź ponownie kod PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Potwierdź swoją tożsamość", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Nie rozpoznano. Spróbuj ponownie.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Sukces", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Anuluj", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Wymagana autoryzacja", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Wymagana biometria", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Wymagane dane logowania urządzenia", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Wymagane dane logowania urządzenia", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Przejdź do ustawień", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Uwierzytelnianie biometryczne nie jest skonfigurowane na tym urządzeniu. Przejdź do 'Ustawienia > Bezpieczeństwo', aby dodać uwierzytelnianie biometryczne.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Uwierzytelnianie biometryczne jest wyłączone. Prosimy zablokować i odblokować ekran, aby je włączyć.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Adres e-mail jest już zarejestrowany.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Adres e-mail nie jest zarejestrowany.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Ten adres e-mail już jest zajęty", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Adres e-mail został zmieniony na {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Uwierzytelnianie nie powiodło się, prosimy spróbować ponownie", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Uwierzytelnianie powiodło się!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sesja wygasła", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Nieprawidłowy klucz odzyskiwania", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Wprowadzony klucz odzyskiwania jest nieprawidłowy", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Pomyślnie zresetowano uwierzytelnianie dwustopniowe", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Twój kod weryfikacyjny wygasł", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Nieprawidłowy kod", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Niestety, wprowadzony kod jest nieprawidłowy", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pt.arb b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb index e1e157fac5..1fcd31bd01 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_pt.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Não foi possível conectar-se ao Ente, verifique suas configurações de rede e entre em contato com o suporte se o erro persistir." -} + "networkHostLookUpErr": "Não foi possível conectar-se ao Ente, verifique suas configurações de rede e entre em contato com o suporte se o erro persistir.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Não foi possível conectar ao Ente, tente novamente após algum tempo. Se o erro persistir, entre em contato com o suporte.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Parece que algo deu errado. Tente novamente mais tarde. Se o erro persistir, entre em contato com nossa equipe de ajuda.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Erro", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Perguntas frequentes", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contatar suporte", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Enviar registros por e-mail", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Envie os logs para \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copiar endereço de e-mail", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exportar logs", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Cancelar", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Informar erro", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Conectado a {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Salvar", + "@save": { + "description": "Label for save button" + }, + "send": "Enviar", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Deseja mesmo salvar isso no armazenamento (pasta de Downloads por padrão) ou enviar a outros aplicativos?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Deseja mesmo salvar em seu armazenamento (pasta de Downloads por padrão)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Insira seu novo e-mail", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "verify": "Verificar", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Endereço de e-mail inválido", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Insira um endereço de e-mail válido.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Aguarde...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verificar senha", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Senha incorreta", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Tente novamente", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Inserir senha", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Insira sua senha", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sessões ativas", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Opa", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Algo deu errado. Tente outra vez", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Isso fará com que você saia deste dispositivo!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Isso fará você sair do dispositivo a seguir:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Sair?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Encerrar", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Esse dispositivo", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Criar conta", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Fraca", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Moderada", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Forte", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Excluir conta", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Estamos tristes por vê-lo sair. Você enfrentou algum problema?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Sim, enviar feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Não, excluir conta", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Autentique-se para iniciar a exclusão de conta", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirmar exclusão de conta", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Esta conta está vinculada a outros apps Ente, se você usa algum.\n\nSeus dados enviados, entre todos os apps Ente, serão marcados para exclusão, e sua conta será apagada permanentemente.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Excluir", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Criar nova conta", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Senha", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirmar senha", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Força da senha: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Como você descobriu o Ente? (opcional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Não rastreamos instalações. Ajudaria bastante se você contasse onde nos achou!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Eu concordo com os termos de serviço e a política de privacidade", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Termos", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Política de Privacidade", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Eu entendo que se eu perder minha senha, posso perder meus dados, já que meus dados são criptografados de ponta a ponta.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Criptografia", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Entrar", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Bem-vindo(a) de volta!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Ao clicar em iniciar sessão, eu concordo com os termos de serviço e a política de privacidade", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Não conectado à internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Verifique sua conexão com a internet e tente novamente.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Falhou na verificação. Tente novamente", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Redefinir senha", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Não é possível verificar a sua senha no dispositivo atual, mas podemos regenerá-la para que funcione em todos os dispositivos. \n\nEntre com a sua chave de recuperação e regenere sua senha (você pode usar a mesma se quiser).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Usar chave de recuperação", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Esqueci a senha", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Alterar e-mail", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verificar e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Enviamos um e-mail à {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Para redefinir sua senha, verifique seu e-mail primeiramente.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Verifique sua caixa de entrada (e spam) para concluir a verificação", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Toque para inserir código", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Enviar e-mail", + "resendEmail": "Reenviar e-mail", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "A verificação ainda está pendente", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sessão expirada", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Sua sessão expirou. Registre-se novamente.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verificação de chave de acesso", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Aguardando verificação...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Tente novamente", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Verificar status", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Registrar com TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Recuperar conta", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Definir senha", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Alterar senha", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Redefinir senha", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Chaves de criptografia", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Insira uma senha que podemos usar para criptografar seus dados", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Insira uma nova senha para criptografar seus dados", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Não salvamos esta senha, então se você esquecê-la, não podemos descriptografar seus dados", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Como funciona", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Gerando chaves de criptografia...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "A senha foi alterada", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Sair da conta em outros dispositivos", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Se você acha que alguém possa saber da sua senha, você pode forçar desconectar sua conta de outros dispositivos.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Sair em outros dispositivos", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Não sair", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Gerando chaves de criptografia...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continuar", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Dispositivo inseguro", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Desculpe, não foi possível gerar chaves de segurança nesse dispositivo.\n\ninicie sessão em um dispositivo diferente.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Chave de recuperação copiada para a área de transferência", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Chave de recuperação", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Caso esqueça sua senha, a única maneira de recuperar seus dados é com esta chave.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Não armazenamos esta chave de 24 palavras. Salve-a em um lugar seguro.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Fazer isso depois", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Salvar chave", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Chave de recuperação salva na pasta Downloads!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Sem chave de recuperação?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Autenticação de dois fatores", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Insira o código de 6 dígitos do aplicativo autenticador", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Perdeu o dispositivo?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Digite a chave de recuperação", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recuperar", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Desconectando...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Imediatamente", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Bloqueio do aplicativo", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Bloqueio automático", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nenhum bloqueio do sistema encontrado", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Para ativar o bloqueio do dispositivo, configure a senha do dispositivo ou o bloqueio de tela nas configurações do seu sistema.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Escolha entre a tela de bloqueio padrão do seu dispositivo e uma tela de bloqueio personalizada com PIN ou senha.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Bloqueio do dispositivo", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN de bloqueio", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Tempo de bloqueio do aplicativo em segundo plano", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Ocultar conteúdo", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Oculta o conteúdo do aplicativo no seletor de aplicativos e desativa as capturas de tela", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Oculta o conteúdo do seletor de aplicativos", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Muitas tentativas incorretas", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Toque para desbloquear", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Deseja mesmo sair?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Sim, quero sair", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Autentique-se para ver suas chaves secretas", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Avançar", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Defina a nova senha", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Inserir PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Definir novo PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirmar", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Reinserir senha", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Reinserir PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verificar identidade", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Não reconhecido. Tente de novo.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Sucesso", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancelar", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Autenticação necessária", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometria necessária", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Credenciais necessários do dispositivo", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Credenciais necessários do dispositivo", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Ir para Opções", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "A autenticação biométrica não está configurada no seu dispositivo. Vá em 'Configurações > Segurança' para adicionar a autenticação biométrica.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "A autenticação biométrica está desativada. Bloqueie e desbloqueie sua tela para ativá-la.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-mail já registrado.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-mail não registrado.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Este e-mail já está em uso", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-mail alterado para {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "A autenticação falhou. Tente novamente", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Autenticado!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sessão expirada", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Chave de recuperação incorreta", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "A chave de recuperação inserida está incorreta", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Autenticação de dois fatores redefinida com sucesso", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Seu código de verificação expirou", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Código incorreto", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "O código inserido está incorreto", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ro.arb b/mobile/packages/strings/lib/l10n/arb/strings_ro.arb new file mode 100644 index 0000000000..d65054a7f0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ro.arb @@ -0,0 +1,311 @@ +{ + "error": "Eroare", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Întrebări frecvente", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Contactează suportul", + "@contactSupport": { + "description": "Contact support button label" + }, + "pleaseSendTheLogsTo": "Te rugăm să trimiți jurnalele la {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copiază adresa de e-mail", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "cancel": "Anulare", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Raportează o eroare", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "save": "Salvare", + "@save": { + "description": "Label for save button" + }, + "send": "Trimitere", + "@send": { + "description": "Label for send button" + }, + "email": "E-mail", + "@email": { + "description": "Email field label" + }, + "invalidEmailTitle": "Adresa e-mail nu este validă", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Te rugăm să introduci o adresă de e-mail validă.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Te rog așteaptă...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Verifică parola", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Parolă incorectă", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Te rugăm să încerci din nou", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Introdu parola", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Introdu parola", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Sesiuni active", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Ceva n-a mers bine, te rog încearcă din nou", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisDevice": "Acest dispozitiv", + "@thisDevice": { + "description": "Label for current device" + }, + "deleteAccount": "Ștergere cont", + "@deleteAccount": { + "description": "Delete account button label" + }, + "yesSendFeedbackAction": "Da, trimite feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nu, șterge contul", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Te rugăm să te autentifici pentru a iniția ștergerea contului", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "delete": "Ștergere", + "@delete": { + "description": "Delete button label" + }, + "password": "Parolă", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirmă parola", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "termsOfServicesTitle": "Termeni", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "welcomeBack": "Bine ai revenit!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "noInternetConnection": "Nu există conexiune la internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "recreatePasswordTitle": "Recreează parola", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "forgotPassword": "Am uitat parola", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Schimbă e-mailul", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verifică e-mail", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Am trimis un e-mail la {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Pentru a reseta parola, te rugăm să confirmi mai întâi adresa de e-mail.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "tapToEnterCode": "Apasă pentru a introduce codul", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Trimite e-mail", + "resendEmail": "Retrimite e-mail", + "@resendEmail": { + "description": "Resend email button label" + }, + "loginSessionExpired": "Sesiune expirată", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "tryAgain": "Încearcă din nou", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Verifică status", + "@checkStatus": { + "description": "Check status button label" + }, + "recoverAccount": "Recuperare cont", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Setează parola", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Schimbă parola", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Resetează parola", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "enterPasswordToEncrypt": "Introdu o parolă pe care o putem folosi pentru a-ți cripta datele", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "passwordWarning": "Nu stocăm această parolă, deci dacă o uiți, nu îți putem decripta datele", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "passwordChangedSuccessfully": "Parola a fost modificată cu succes", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Deconectare de pe alte dispozitive", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherDevices": "Deconectează alte dispozitive", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Nu te deconecta", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "recoveryKeyCopiedToClipboard": "Cheie de recuperare salvată în clipboard", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Cheie de recuperare", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Dacă îți uiți parola, singura modalitate prin care poți recupera datele este cu această cheie.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Nu stocăm această cheie, vă rugăm salvați această cheie de 24 de cuvinte într-un loc sigur.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "saveKey": "Salvare cheie", + "@saveKey": { + "description": "Save key button label" + }, + "twoFactorAuthTitle": "Autentificare cu doi factori", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "recover": "Recuperează", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Deconectare...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "androidBiometricHint": "Verifică identitatea", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Neidentificat. Încearcă din nou.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Succes", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Anulare", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Autentificare necesară", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "goToSettings": "Mergi la setări", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "iOSOkButton": "Ok", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Această adresă de e-mail este deja folosită", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-mail modificat în {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "sessionExpired": "Sesiune expirată", + "@sessionExpired": { + "description": "Error message when session has expired" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ru.arb b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb index 9ee758ceda..3ae06044a0 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ru.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Не удается подключиться к Ente, пожалуйста, проверьте настройки своей сети и обратитесь в службу поддержки, если ошибка повторится." -} + "networkHostLookUpErr": "Не удается подключиться к Ente, пожалуйста, проверьте настройки своей сети и обратитесь в службу поддержки, если ошибка повторится.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Не удается подключиться к Ente, пожалуйста, повторите попытку через некоторое время. Если ошибка не устраняется, обратитесь в службу поддержки.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Похоже, что-то пошло не так. Пожалуйста, повторите попытку через некоторое время. Если ошибка повторится, обратитесь в нашу службу поддержки.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Ошибка", + "@error": { + "description": "Generic error title" + }, + "ok": "Ок", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "ЧаВо", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Связаться с поддержкой", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Отправить свои журналы", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Пожалуйста, отправьте журналы на \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Копировать адрес электронной почты", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Экспорт журналов", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Отмена", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Сообщить об ошибке", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Подключено к {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Сохранить", + "@save": { + "description": "Label for save button" + }, + "send": "Отправить", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Вы хотите сохранить это в хранилище (папку загрузок по умолчанию) или отправить в другие приложения?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Вы хотите сохранить это в хранилище (по умолчанию папка загрузок)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Введите ваш новый адрес электронной почты", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Электронная почта", + "@email": { + "description": "Email field label" + }, + "verify": "Подтвердить", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Неверный адрес электронной почты", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Пожалуйста, введите действительный адрес электронной почты.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Пожалуйста, подождите...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Подтверждение пароля", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Неправильный пароль", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Пожалуйста, попробуйте ещё раз", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Введите пароль", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Введите пароль", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Активные сеансы", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ой", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Что-то пошло не так. Попробуйте еще раз", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Вы выйдете из этого устройства!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Вы выйдете из списка следующих устройств:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Завершить сеанс?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Завершить", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Это устройство", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Создать аккаунт", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Слабый", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Средний", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Сильный", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Удалить аккаунт", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Нам будет жаль, если вы уйдете. Вы столкнулись с какой-то проблемой?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Да, отправить отзыв", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Нет, удалить аккаунт", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Пожалуйста, авторизуйтесь, чтобы начать удаление аккаунта", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Подтвердить удаление аккаунта", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Эта учетная запись связана с другими приложениями Ente, если вы ими пользуетесь.\n\nЗагруженные вами данные во всех приложениях Ente будут запланированы к удалению, а ваша учетная запись будет удалена без возможности восстановления.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Удалить", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Создать новый аккаунт", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Пароль", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Подтвердить пароль", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Мощность пароля: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Как вы узнали о Ente? (необязательно)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Мы не отслеживаем установки приложений. Было бы полезно, если бы вы сказали, где нас нашли!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Я согласен с условиями предоставления услуг и политикой конфиденциальности", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Условия использования", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Политика конфиденциальности", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Я понимаю, что если я потеряю свой пароль, я могу потерять свои данные, так как мои данные в сквозном шифровании.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Шифрование", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Войти", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "С возвращением!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Нажимая на логин, я принимаю условия использования и политику конфиденциальности", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Нет подключения к Интернету", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Проверьте подключение к Интернету и повторите попытку.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Проверка не удалась, попробуйте еще раз", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Пересоздать пароль", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Текущее устройство недостаточно мощно для верификации пароля, но мы можем регенерировать так, как это работает со всеми устройствами.\n\nПожалуйста, войдите, используя ваш ключ восстановления и сгенерируйте ваш пароль (вы можете использовать тот же пароль, если пожелаете).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Использовать ключ восстановления", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Забыл пароль", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Изменить адрес электронной почты", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Подтвердить адрес электронной почты", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Мы отправили письмо на {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Подтвердите адрес электронной почты, чтобы сбросить пароль.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Пожалуйста, проверьте свой почтовый ящик (и спам) для завершения верификации", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Нажмите, чтобы ввести код", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Отправить электронное письмо", + "resendEmail": "Отправить письмо еще раз", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Верификация еще не завершена", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Сессия недействительна", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Сессия истекла. Войдите снова.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Проверка с помощью ключа доступа", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Ожидание подтверждения...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Попробовать снова", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Проверить статус", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Войти с помощью TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Восстановить аккаунт", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Поставить пароль", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Изменить пароль", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Сбросить пароль", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Ключи шифрования", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Введите пароль, который мы можем использовать для шифрования ваших данных", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Введите новый пароль, который мы можем использовать для шифрования ваших данных", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Мы не храним этот пароль, поэтому если вы забудете его, мы не сможем расшифровать ваши данные", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Как это работает", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Генерируем ключи шифрования...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Пароль успешно изменён", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Выйти из других устройств", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Если вы думаете, что кто-то может знать ваш пароль, вы можете принудительно выйти из всех устройств.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Выйти из других устройств", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Не выходить", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Генерируем ключи шифрования...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Далее", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Небезопасное устройство", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "К сожалению, мы не смогли сгенерировать безопасные ключи на этом устройстве.\n\nПожалуйста, зарегистрируйтесь с другого устройства.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Ключ восстановления скопирован в буфер обмена", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Ключ восстановления", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Если вы забыли свой пароль, то восстановить данные можно только с помощью этого ключа.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Мы не храним этот ключ, пожалуйста, сохраните этот ключ в безопасном месте.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Сделать позже", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Сохранить ключ", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Ключ восстановления сохранён в папке Загрузки!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Нет ключа восстановления?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Двухфакторная аутентификация", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Введите 6-значный код из\nвашего приложения-аутентификатора", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Потеряно устройство?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Введите ключ восстановления", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Восстановить", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Выходим...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Немедленно", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Блокировка приложения", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Автоблокировка", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Системная блокировка не найдена", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Чтобы включить блокировку устройства, пожалуйста, настройте пароль или блокировку экрана в настройках системы.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Выберите между экраном блокировки вашего устройства и пользовательским экраном блокировки с PIN-кодом или паролем.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Блокировка устройства", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin блокировка", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Время в фоне, после которого приложение блокируется", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Скрыть содержимое", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Скрывает содержимое приложения в переключателе приложений и отключает скриншоты", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Скрывает содержимое приложения в переключателе приложений", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Слишком много неудачных попыток", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Нажмите для разблокировки", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Вы уверены, что хотите выйти?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Да, выйти", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Пожалуйста, авторизуйтесь для просмотра ваших секретов", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Далее", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Задать новый пароль", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Введите PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Установите новый PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Подтвердить", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Подтвердите пароль", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Введите PIN-код ещё раз", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Подтвердите личность", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Не распознано. Попробуйте еще раз.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Успешно", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Отменить", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Требуется аутентификация", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Требуется биометрия", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Требуются учетные данные устройства", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Требуются учетные данные устройства", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Перейдите к настройкам", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Биометрическая аутентификация не настроена на вашем устройстве. Перейдите в \"Настройки > Безопасность\", чтобы добавить биометрическую аутентификацию.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Биометрическая аутентификация отключена. Пожалуйста, заблокируйте и разблокируйте экран, чтобы включить ее.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "ОК", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Адрес электронной почты уже зарегистрирован.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Адрес электронной почты не зарегистрирован.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Этот адрес электронной почты уже используется", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Адрес электронной почты изменен на {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Аутентификация не удалась, попробуйте еще раз", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Аутентификация прошла успешно!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Сеанс истек", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Неправильный ключ восстановления", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Введен неправильный ключ восстановления", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Двухфакторная аутентификация успешно сброшена", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Срок действия вашего проверочного кода истек", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Неверный код", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Извините, введенный вами код неверный", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sk.arb b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb index 6f020d25be..6ab9ffae97 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sk.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb @@ -1,3 +1,692 @@ { - "networkHostLookUpErr": "Nemožno sa pripojiť k Ente, skontrolujte svoje nastavenia siete a kontaktujte podporu, ak chyba pretrváva." -} + "networkHostLookUpErr": "Nemožno sa pripojiť k Ente, skontrolujte svoje nastavenia siete a kontaktujte podporu, ak chyba pretrváva.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Nemožno sa pripojiť k Ente, skúste znova v krátkom čase. Ak chyba pretrváva, kontaktujte podporu.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Vyzerá to, že sa niečo pokazilo. Skúste znova v krátkom čase. Ak chyba pretrváva, kontaktujte náš tím podpory.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Chyba", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Často kladené otázky", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Kontaktovať podporu", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Odoslať vaše logy emailom", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Prosím, pošlite logy na adresu \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Skopírovať e-mailovú adresu", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exportovať logy", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Zrušiť", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Nahlásiť chybu", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Pripojený k endpointu {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Uložiť", + "@save": { + "description": "Label for save button" + }, + "send": "Odoslať", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory) alebo to odoslať do iných aplikácií?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Overiť", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Neplatná emailová adresa", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Zadajte platnú e-mailovú adresu.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Prosím počkajte...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Potvrďte heslo", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Nesprávne heslo", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Prosím, skúste to znova", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Zadajte heslo", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Zadajte vaše heslo", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktívne relácie", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Niečo sa pokazilo, skúste to prosím znova", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Toto vás odhlási z tohto zariadenia!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Toto vás odhlási z následujúceho zariadenia:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Ukončiť reláciu?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Ukončiť", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Toto zariadenie", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Vytvoriť účet", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Slabé", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Mierne", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Silné", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Odstrániť účet", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Bude nám ľúto ak odídeš. Máš nejaký problém?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Áno, odoslať spätnú väzbu", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nie, odstrániť účet", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Je potrebné overenie pre spustenie odstránenia účtu", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Potvrď odstránenie účtu", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používaš.\n\nTvoje nahrané údaje vo všetkých Ente aplikáciách budú naplánované na odstránenie a tvoj účet bude natrvalo odstránený.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Odstrániť", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Vytvoriť nový účet", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Heslo", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Potvrdiť heslo", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Sila hesla: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Ako ste sa dozvedeli o Ente? (voliteľné)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Nesledujeme inštalácie aplikácie. Veľmi by nám pomohlo, keby ste nám povedali, ako ste sa o nás dozvedeli!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Súhlasím s podmienkami používania a zásadami ochrany osobných údajov", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Podmienky používania", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Zásady ochrany osobných údajov", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Rozumiem, že ak stratím alebo zabudnem heslo, môžem stratiť svoje údaje, pretože moje údaje sú šifrované end-to-end.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Šifrovanie", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Prihlásenie", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Vitajte späť!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Kliknutím na prihlásenie, súhlasím s podmienkami používania a zásadami ochrany osobných údajov", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Žiadne internetové pripojenie", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Skontrolujte svoje internetové pripojenie a skúste to znova.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Overenie zlyhalo, skúste to znova", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Resetovať heslo", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Aktuálne zariadenie nie je dostatočne výkonné na overenie vášho hesla, avšak vieme ho regenerovať spôsobom, ktorý funguje vo všetkých zariadeniach.\n\nPrihláste sa pomocou kľúča na obnovenie a znovu vygenerujte svoje heslo (ak si prajete, môžete znova použiť rovnaké).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Použiť kľúč na obnovenie", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Zabudnuté heslo", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Zmeniť e-mail", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Overiť email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Odoslali sme email na adresu {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Ak chcete obnoviť svoje heslo, najskôr overte svoj email.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Skontrolujte svoju doručenú poštu (a spam) pre dokončenie overenia", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Klepnutím zadajte kód", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Odoslať email", + "resendEmail": "Znovu odoslať email", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Overenie stále prebieha", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Relácia vypršala", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Vaša relácia vypršala. Prosím, prihláste sa znovu.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Overenie pomocou passkey", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Čakanie na overenie...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Skúsiť znova", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Overiť stav", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Prihlásenie pomocou TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Obnoviť účet", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Nastaviť heslo", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Zmeniť heslo", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Obnoviť heslo", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Šifrovacie kľúče", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Zadajte heslo, ktoré môžeme použiť na šifrovanie vašich údajov", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Zadajte nové heslo, ktoré môžeme použiť na šifrovanie vašich údajov", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Ente neukladá tohto heslo. V prípade, že ho zabudnete, nie sme schopní rozšifrovať vaše údaje", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Ako to funguje", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generovanie šifrovacích kľúčov...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Heslo bolo úspešne zmenené", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Odhlásiť sa z iných zariadení", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Ak si myslíš, že by niekto mohol poznať tvoje heslo, môžeš vynútiť odhlásenie všetkých ostatných zariadení používajúcich tvoj účet.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Odhlásiť iné zariadenie", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Neodhlasovať", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generovanie šifrovacích kľúčov...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Pokračovať", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Slabo zabezpečené zariadenie", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Ospravedlňujeme sa, v tomto zariadení sme nemohli generovať bezpečnostné kľúče.\n\nzaregistrujte sa z iného zariadenia.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Skopírovaný kód pre obnovenie do schránky", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Kľúč pre obnovenie", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Ak zabudnete heslo, jediným spôsobom, ako môžete obnoviť svoje údaje, je tento kľúč.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "My tento kľúč neuchovávame, uložte si tento kľúč obsahujúci 24 slov na bezpečnom mieste.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Urobiť to neskôr", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Uložiť kľúč", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Kľúč na obnovenie uložený v priečinku Stiahnutých súborov!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Nemáte kľúč pre obnovenie?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Dvojfaktorové overovanie", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Zadajte 6-miestny kód z\nvašej overovacej aplikácie", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Stratené zariadenie?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Vložte váš kód pre obnovenie", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Obnoviť", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Odhlasovanie...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Okamžite", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Zámok aplikácie", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatické uzamknutie", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nenájdená žiadna zámka obrazovky", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Pre povolenie zámku zariadenia, nastavte prístupový kód zariadenia alebo zámok obrazovky v nastaveniach systému.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Vyberte si medzi predvolenou zámkou obrazovky vášho zariadenia a vlastnou zámkou obrazovky s PIN kódom alebo heslom.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Zámok zariadenia", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Zámok PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Čas, po ktorom sa aplikácia uzamkne po nečinnosti", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Skryť obsah", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Skrýva obsah v prepínači aplikácii a zakazuje snímky obrazovky", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Skrýva obsah v prepínači aplikácii", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Príliš veľa chybných pokusov", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Ťuknutím odomknete", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Naozaj sa chcete odhlásiť?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Áno, odhlásiť sa", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Pre zobrazenie vašich tajných údajov sa musíte overiť", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Ďalej", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Nastaviť nové heslo", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Zadajte PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Nastaviť nový PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Potvrdiť", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Zadajte heslo znova", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Zadajte PIN znova", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Overiť identitu", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Nerozpoznané. Skúste znova.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Overenie úspešné", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Zrušiť", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Vyžaduje sa overenie", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Vyžaduje sa biometria", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Vyžadujú sa poverenia zariadenia", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Vyžadujú sa poverenia zariadenia", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Prejsť do nastavení", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Overenie pomocou biometrie nie je na vašom zariadení nastavené. Prejdite na 'Nastavenie > Zabezpečenie' a pridajte overenie pomocou biometrie.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Overenie pomocou biometrie je zakázané. Zamknite a odomknite svoju obrazovku, aby ste ho povolili.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Tento e-mail sa už používa", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Emailová adresa bola zmenená na {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Overenie zlyhalo. Skúste to znova", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Overenie sa podarilo!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Relácia vypršala", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Nesprávny kľúč na obnovenie", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Kľúč na obnovenie, ktorý ste zadali, je nesprávny", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Dvojfaktorové overovanie bolo úspešne obnovené", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Platnosť overovacieho kódu uplynula", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Neplatný kód", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Ľutujeme, zadaný kód je nesprávny", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sl.arb b/mobile/packages/strings/lib/l10n/arb/strings_sl.arb new file mode 100644 index 0000000000..8112048df0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_sl.arb @@ -0,0 +1,700 @@ +{ + "networkHostLookUpErr": "Ne morete se povezati z Ente, preverite omrežne nastavitve in se obrnite na podporo, če se napaka nadaljuje.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Ne morete se povezati z Ente, poskusite znova čez nekaj časa. Če se napaka nadaljuje, se obrnite na podporo.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Zdi se, da je šlo nekaj narobe. Po določenem času poskusite znova. Če se napaka nadaljuje, se obrnite na našo ekipo za podporo.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Napaka", + "@error": { + "description": "Generic error title" + }, + "ok": "V redu", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Pogosta vprašanja", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Stik s podporo", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Pošlji loge po e-pošti", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Loge pošljite na naslov \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopiraj e-poštni naslov", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Izvozi loge", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Prekliči", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Prijavite napako", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Povezano na {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Shrani", + "@save": { + "description": "Label for save button" + }, + "send": "Pošlji", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Želite to shraniti v shrambo (privzeto: mapa Prenosi) ali poslati drugim aplikacijam?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Želite to shraniti v shrambo (privzeto: mapa Prenosi)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "E-pošta", + "@email": { + "description": "Email field label" + }, + "verify": "Preveri", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Neveljaven e-poštni naslov", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Prosimo vnesite veljaven e-poštni naslov.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Prosim počakajte...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Potrdite geslo", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Nepravilno geslo", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Prosimo, poskusite ponovno", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Vnesite geslo", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Vnesite svoje geslo", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktivne seje", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Ups", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Nekaj je šlo narobe, prosimo poizkusite znova.", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "To vas bo odjavilo iz te naprave!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "To vas bo odjavilo iz naslednje naprave:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Končaj sejo", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Končaj", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Ta naprava", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Ustvari račun", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Šibko", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Zmerno", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Močno", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Izbriši račun", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Žal nam je, da odhajate. Imate kakšne težave?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ja, pošlji povratne informacije", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Ne, izbriši račun", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Za izbris računa, se overite", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Potrdi brisanje računa", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Ta račun je povezan z drugimi aplikacijami Ente, če jih uporabljate.\n\nVaši naloženi podatki v vseh aplikacijah Ente bodo načrtovane za izbris, vaš račun pa bo trajno izbrisan.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Ustvari nov račun", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Geslo", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Potrdi geslo", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Moč gesla: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Kako ste slišali o Ente? (izbirno)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Namestitvam aplikacij ne sledimo. Pomagalo bi, če bi nam povedali, kje ste nas našli!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Strinjam se s pogoji uporabe in politiko zasebnosti", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Pogoji uporabe", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Politika zasebnosti", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Razumem, da lahko z izgubo gesla, izgubim svoje podatke, saj so end-to-end šifrirani", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Šifriranje", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Prijava", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Dobrodošli nazaj!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "S klikom na prijava, se strinjam s pogoji uporabe in politiko zasebnosti", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Ni internetne povezave", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Preverite internetno povezavo in poskusite znova.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Potrjevanje ni bilo uspešno, prosimo poskusite znova.", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Ponovno ustvarite geslo", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Trenutna naprava, ni dovolj zmogljiva za preverjanje vašega gesla, a ga lahko generiramo na način, ki deluje z vsemi napravami.\n\nProsimo, prijavite se z vašim ključem za obnovo in ponovno ustvarite geslo (če želite lahko uporabite enako kot prej).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Uporabi ključ za obnovo", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Pozabljeno geslo", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Sprememba e-poštnega naslova", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Potrdite e-pošto", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Poslali smo e-pošto na {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Če želite ponastaviti geslo, najprej potrdite svoj e-poštni naslov.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Prosimo, preverite svoj e-poštni predal (in nezaželeno pošto), da končate verifikacijo", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Pritisni za vnos kode", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Pošlji e-pošto", + "resendEmail": "Ponovno pošlji e-pošto", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Preverjanje še ni zaključeno", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Seja je potekla", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Vaša seja je potekla. Prosimo ponovno se prijavite.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Potrditev ključa za dostop (passkey)", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Čakanje na potrditev...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Poskusite ponovno", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Preveri status", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Prijava z TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Obnovi račun", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Nastavite geslo", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Sprememba gesla", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Ponastavitev gesla", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Šifrirni ključi", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Vnesite geslo, ki ga lahko uporabimo za šifriranje vaših podatkov", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Vnesite novo geslo, ki ga lahko uporabimo za šifriranje vaših podatkov", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Tega gesla ne shranjujemo, zato v primeru, da ga pozabite, ne moremo dešifrirati vaših podatkov.", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Kako deluje? ", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Ustvarjanje ključe za šifriranje", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Geslo je bilo uspešno spremenjeno", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Odjavi se iz ostalih naprav", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Če menite, da bi lahko kdo poznal vaše geslo, lahko vse druge naprave, ki uporabljajo vaš račun, prisilite, da se odjavijo.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Odjavi ostale naprave", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Ne odjavi se", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Generiramo ključe za šifriranje", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Nadaljuj", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Nezanesljiva naprava", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Žal v tej napravi nismo mogli ustvariti varnih ključev.\n\nProsimo, prijavite se iz druge naprave.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Ključ za obnovo kopiran v odložišče", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Ključ za obnovitev", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Če pozabite svoje geslo, je edini način da obnovite svoje podatke s tem ključem", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Tega ključa ne hranimo, prosimo shranite teh 24 besed na varnem", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Stori to kasneje", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Shrani ključ", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Ključ za obnovitev je shranjen v mapi Prenosi!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Nimate ključa za obnovo?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Dvojno preverjanja pristnosti", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Vnesite 6 mestno kodo iz vaše aplikacije za preverjanje pristnosti", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Izgubljena naprava?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Vnesite vaš ključ za obnovitev", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Obnovi", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Odjavljanje...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Takoj", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Zaklep aplikacije", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Samodejno zaklepanje", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Nobeno zaklepanje sistema ni bilo najdeno", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Da omogočite zaklepanje naprave, prosimo nastavite kodo ali zaklepanje zaslona v sistemskih nastavitvah.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Izbirate lahko med privzetim zaklenjenim zaslonom naprave in zaklenjenim zaslonom po meri s kodo PIN ali geslom.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Zaklepanje naprave", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Zaklepanje s PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Čas po katerem se aplikacije zaklene, ko jo enkrat zapustite.", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Skrij vsebino", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Skrije vsebino aplikacije v menjalniku opravil in onemogoči posnetke zaslona", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Skrije vsebino aplikacije v menjalniku opravil", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Preveč nepravilnih poskusov", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Kliknite za odklepanje", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Ali ste prepričani, da se želite odjaviti?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ja, odjavi se", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Če si želite ogledati svoje skrivne ključe, se overite", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Naprej", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Nastavi novo geslo", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Vnesi PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Nastavi nov PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Potrdi", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Ponovno vnesite geslo", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Ponovno vnesite PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Potrdite identiteto", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Ni prepoznano. Poskusite znova.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Uspešno", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Prekliči", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Potrebna je overitev", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Zahtevani biometrični podatki", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Zahtevani podatki za vpis v napravo", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Zahtevani podatki za vpis v napravo", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Pojdi v nastavitve", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometrično overjanje v vaši napravi ni nastavljeno. Pojdite v \"Nastavitve > Varnost\" in dodajte biometrično overjanje.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrično overjanje je onemogočeno. Če ga želite omogočiti, zaklenite in odklenite zaslon.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "V redu", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-poštni naslov je že registriran.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-poštni naslov ni registriran.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Ta e-poštni naslove je že v uporabi.", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-poštni naslove je bil spremenjen na {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Overitev ni uspela, prosimo poskusite znova", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Overitev uspešna!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Seja je potekla", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Nepravilen ključ za obnovitev", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Ključ za obnovitev, ki ste ga vnesli ni pravilen", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Uspešna ponastavitev dvostopenjske avtentikacije", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Vaša koda za potrditev je potekla.", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Nepravilna koda", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Oprostite, koda ki ste jo vnesli ni pravilna", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sr.arb b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb index a133eadd43..8d8f41e93e 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Није могуће повезивање са Ente-ом, молимо вас да проверите мрежне поставке и контактирајте подршку ако грешка и даље постоји." -} + "networkHostLookUpErr": "Није могуће повезивање са Ente-ом, молимо вас да проверите мрежне поставке и контактирајте подршку ако грешка и даље постоји.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Није могуће повезивање са Ente-ом, покушајте поново мало касније. Ако грешка настави, обратите се подршци.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Изгледа да је нешто погрешно. Покушајте поново након неког времена. Ако грешка настави, обратите се нашем тиму за подршку.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Грешка", + "@error": { + "description": "Generic error title" + }, + "ok": "У реду", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Питања", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Контактирати подршку", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Имејлирајте извештаје", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Пошаљите извештаје на \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Копирати имејл адресу", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Извези изештаје", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Откажи", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Пријави грешку", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Везано за {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Сачувај", + "@save": { + "description": "Label for save button" + }, + "send": "Пошаљи", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано) или да га пошаљете другим апликацијама?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Унесите Ваш нови имејл", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "Имејл", + "@email": { + "description": "Email field label" + }, + "verify": "Верификуј", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Погрешна имејл адреса", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Унесите важећи имејл.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Молимо сачекајте...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Верификујте лозинку", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Неисправна лозинка", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Пробајте поново", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Унеси лозинку", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Унесите лозинку", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Активне сесије", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Упс", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Нешто је пошло наопако. Покушајте поново", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Ово ће вас одјавити из овог уређаја!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Ово ће вас одјавити из овог уређаја:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Прекинути сесију?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Прекини", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Овај уређај", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Направи налог", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Слабо", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Умерено", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Јако", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Избриши налог", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Жао нам је што одлазите. Да ли се суочавате са неком грешком?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Да, послати повратне информације", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Не, избрисати налог", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Молимо вас да се аутентификујете за брисање рачуна", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Потврда брисања рачуна", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Овај налог је повезан са другим Ente апликацијама, ако користите било коју.\n\nВаши преношени подаци, на свим Ente апликацијама биће заказани за брисање, и ваш рачун ће се трајно избрисати.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Обриши", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Креирај нови налог", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Лозинка", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Потврдите лозинку", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Снага лозинке: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Како сте чули о Ente? (опционо)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Не пратимо инсталацију апликације. Помогло би да нам кажеш како си нас нашао!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Прихватам услове сервиса и политику приватности", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Услови", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Политика приватности", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Разумем да ако изгубим лозинку, могу изгубити своје податке пошто су шифрирани од краја до краја.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Шифровање", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Пријави се", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Добродошли назад!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Кликом на пријаву, прихватам услове сервиса и политику приватности", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Нема интернет везе", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Провери своју везу са интернетом и покушај поново.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Неуспешна верификација, покушајте поново", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Поново креирати лозинку", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Тренутни уређај није довољно моћан да потврди вашу лозинку, али можемо регенерирати на начин који ради са свим уређајима.\n\nПријавите се помоћу кључа за опоравак и обновите своју лозинку (можете поново користити исту ако желите).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Користите кључ за опоравак", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Заборавио сам лозинку", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Промени имејл", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Потврди имејл", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Послали смо имејл на {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Да бисте ресетовали лозинку, прво потврдите свој имејл.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Молимо вас да проверите примљену пошту (и нежељену пошту) да бисте довршили верификацију", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Пипните да бисте унели кôд", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Шаљи имејл", + "resendEmail": "Поново послати имејл", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Верификација је још у току", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Сесија је истекла", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Ваша сесија је истекла. Молимо пријавите се поново.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Верификација сигурносном кључем", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Чека се верификација...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Покушај поново", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Провери статус", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Пријава са TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Опоравак налога", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Постави лозинку", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Промени лозинку", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Ресетуј лозинку", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Кључеве шифровања", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Унесите лозинку за употребу за шифровање ваших података", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Унесите нову лозинку за употребу за шифровање ваших података", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Не чувамо ову лозинку, па ако је заборавите, не можемо дешифрирати ваше податке", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Како то функционише", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Генерисање кључева за шифровање...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Лозинка је успешно промењена", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Одјави се из других уређаја", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Ако мислиш да неко може знати твоју лозинку, можеш приморати одјављивање све остале уређаје које користе твој налог.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Одјави друге уређаје", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Не одјави", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Генерисање кључева за шифровање...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Настави", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Уређај није сигуран", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Извините, не можемо да генеришемо сигурне кључеве на овом уређају.\n\nМолимо пријавите се са другог уређаја.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Кључ за опоравак копирано у остави", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Резервни Кључ", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Ако заборавите лозинку, једини начин на који можете повратити податке је са овим кључем.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Не чувамо овај кључ, молимо да сачувате кључ од 24 речи на сигурном месту.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Уради то касније", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Сачувај кључ", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Кључ за опоравак сачуван у фасцикли за преузимање!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Немате кључ за опоравак?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Дво-факторска аутентификација", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Унесите 6-цифрени кôд из\nапликације за аутентификацију", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Узгубили сте уређај?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Унети кључ за опоравак", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Опорави", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Одјављивање...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Одмах", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Закључавање апликације", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Ауто-закључавање", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Није пронађено ниједно закључавање система", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Да бисте омогућили закључавање уређаја, молимо вас да подесите шифру уређаја или закључавање екрана у системским подешавањима.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Изаберите између заданог закључавање екрана вашег уређаја и прилагођени екран за закључавање са ПИН-ом или лозинком.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Закључавање уређаја", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "ПИН клокирање", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Време након којег се апликација блокира након што је постављенеа у позадину", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Сакриј садржај", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Сакрива садржај апликације у пребацивање апликација и онемогућује снимке екрана", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Сакрива садржај апликације у пребацивање апликација", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Превише погрешних покушаја", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Додирните да бисте откључали", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Да ли сте сигурни да се одјавите?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Да, одјави ме", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Аутентификујте се да бисте прегледали Ваше тајне", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Следеће", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Постави нову лозинку", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Унеси ПИН", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Постави нови ПИН", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Потврди", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Поново унеси лозинку", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Поново унеси ПИН", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Потврдите идентитет", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Нисмо препознали. Покушати поново.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Успех", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Откажи", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Потребна аутентификација", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Потребна је биометрија", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Потребни су акредитиви уређаја", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Потребни су акредитиви уређаја", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Иди на поставке", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Биометријска аутентификација није постављена на вашем уређају. Идите на \"Подешавања> Сигурност\" да бисте додали биометријску аутентификацију.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Биометријска аутентификација је онемогућена. Закључајте и откључите екран да бисте је омогућили.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "У реду", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Имејл је већ регистрован.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Имејл није регистрован.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Овај имејл је већ у употреби", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Имејл промењен на {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Аутентификација није успела, покушајте поново", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Успешна аутентификација!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Сесија је истекла", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Нетачан кључ за опоравак", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Унети кључ за опоравак је натачан", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Двофакторска аутентификација успешно рисетирана", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Ваш верификациони кôд је истекао", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Погрешан кôд", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Унет кôд није добар", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sv.arb b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb index 8daede564e..6940c3eedb 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sv.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb @@ -1,3 +1,696 @@ { - "networkHostLookUpErr": "Det gick inte att ansluta till Ente, kontrollera dina nätverksinställningar och kontakta supporten om felet kvarstår." -} + "networkHostLookUpErr": "Det gick inte att ansluta till Ente, kontrollera dina nätverksinställningar och kontakta supporten om felet kvarstår.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Det gick inte att ansluta till Ente, försök igen om en stund. Om felet kvarstår, vänligen kontakta support.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Det ser ut som om något gick fel. Försök igen efter en stund. Om felet kvarstår, vänligen kontakta vår support.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Fel", + "@error": { + "description": "Generic error title" + }, + "ok": "OK", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Kontakta support", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Maila dina loggar", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Vänligen skicka loggarna till \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Kopiera e-postadress", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Exportera loggar", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Avbryt", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Rapportera en bugg", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Ansluten till {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Spara", + "@save": { + "description": "Label for save button" + }, + "send": "Skicka", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard) eller skicka den till andra appar?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Ange din nya e-postadress", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-post", + "@email": { + "description": "Email field label" + }, + "verify": "Verifiera", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Ogiltig e-postadress", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Ange en giltig e-postadress.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Vänligen vänta...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Bekräfta lösenord", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Felaktigt lösenord", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Försök igen", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Ange lösenord", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Ange ditt lösenord", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktiva sessioner", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Hoppsan", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Något gick fel, vänligen försök igen", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Detta kommer att logga ut dig från den här enheten!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Detta kommer att logga ut dig från följande enhet:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Avsluta session?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Avsluta", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Den här enheten", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Skapa konto", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Svag", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Måttligt", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Stark", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Radera konto", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Vi kommer att vara ledsna över att se dig gå. Har du något problem?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Ja, skicka feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Nej, radera konto", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Vänligen autentisera för att initiera borttagning av konto", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Bekräfta radering av kontot", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Radera", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Skapa nytt konto", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Lösenord", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Bekräfta lösenord", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Lösenordsstyrka: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Hur hörde du talas om Ente? (valfritt)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Jag samtycker till användarvillkoren och integritetspolicyn", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Villkor", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Integritetspolicy", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är end-to-end-krypterad.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Kryptering", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Logga in", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Välkommen tillbaka!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Jag samtycker till användarvillkoren och integritetspolicyn", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Ingen internetanslutning", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Kontrollera din internetanslutning och försök igen.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verifiering misslyckades, vänligen försök igen", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Återskapa lösenord", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Denna enhet är inte tillräckligt kraftfull för att verifiera ditt lösenord, men vi kan återskapa det på ett sätt som fungerar med alla enheter.\n\nLogga in med din återställningsnyckel och återskapa ditt lösenord (du kan använda samma igen om du vill).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Använd återställningsnyckel", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Glömt lösenord", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Ändra e-postadress", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verifiera e-postadress", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Vi har skickat ett mail till {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "För att återställa ditt lösenord måste du först bekräfta din e-postadress.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tryck för att ange kod", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Skicka e-post", + "resendEmail": "Skicka e-post igen", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Verifiering pågår fortfarande", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Sessionen har gått ut", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Din session har upphört. Logga in igen.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Verifiering med inloggningsnyckel", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Väntar på verifiering...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Försök igen", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Kontrollera status", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Logga in med TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Återställ konto", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Ställ in lösenord", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Ändra lösenord", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Återställ lösenord", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Krypteringsnycklar", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Ange ett lösenord som vi kan använda för att kryptera din data", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Ange ett nytt lösenord som vi kan använda för att kryptera din data", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Vi lagrar inte detta lösenord, så om du glömmer bort det, kan vi inte dekryptera dina data", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Så här fungerar det", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Skapar krypteringsnycklar...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Lösenordet har ändrats", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Logga ut från andra enheter", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Om du tror att någon kanske känner till ditt lösenord kan du tvinga alla andra enheter med ditt konto att logga ut.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Logga ut andra enheter", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Logga inte ut", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Skapar krypteringsnycklar...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Fortsätt", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Osäker enhet", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Återställningsnyckel kopierad till urklipp", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Återställningsnyckel", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Gör detta senare", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Spara nyckel", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Återställningsnyckel sparad i nedladdningsmappen!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Ingen återställningsnyckel?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Tvåfaktorsautentisering", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Ange den 6-siffriga koden från din autentiseringsapp", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Förlorad enhet?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Ange din återställningsnyckel", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Återställ", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Loggar ut...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Omedelbart", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Applås", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Automatisk låsning", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Inget systemlås hittades", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "För att aktivera enhetslås, vänligen ställ in enhetens lösenord eller skärmlås i dina systeminställningar.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "deviceLock": "Enhetslås", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pinkodslås", + "@pinLock": { + "description": "PIN lock option title" + }, + "hideContent": "Dölj innehåll", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Döljer appinnehåll i app-växlaren", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "För många felaktiga försök", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tryck för att låsa upp", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Är du säker på att du vill logga ut?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Ja, logga ut", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Autentisera för att visa din återställningsnyckel", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Nästa", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Ställ in nytt lösenord", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Ange PIN-kod", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Ställ in ny PIN-kod", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Bekräfta", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Ange lösenord igen", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Ange PIN-kod igen", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verifiera identitet", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Ej godkänd. Försök igen.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Slutförd", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Avbryt", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Obligatorisk autentisering", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometriska uppgifter krävs", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Enhetsuppgifter krävs", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Enhetsuppgifter krävs", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Gå till inställningar", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometrisk autentisering är inte konfigurerad på din enhet. Gå till \"Inställningar > Säkerhet\" för att lägga till biometrisk autentisering.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometrisk autentisering är inaktiverat. Lås och lås upp din skärm för att aktivera den.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-postadress redan registrerad.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-postadress ej registrerad.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Denna e-postadress används redan", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-post ändrad till {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Autentisering misslyckades, vänligen försök igen", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Autentisering lyckades!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Sessionen har gått ut", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Felaktig återställningsnyckel", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Återställningsnyckeln du angav är felaktig", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Tvåfaktorsautentisering återställd", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Din verifieringskod har upphört att gälla", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Felaktig kod", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Tyvärr, den kod som du har angett är felaktig", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ti.arb b/mobile/packages/strings/lib/l10n/arb/strings_ti.arb new file mode 100644 index 0000000000..a8a44c1d38 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_ti.arb @@ -0,0 +1,542 @@ +{ + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Error", + "@error": { + "description": "Generic error title" + }, + "ok": "Ok", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "FAQ", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "ደገፍ ኣድራሻ", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Email your logs", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Please send the logs to \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Copy email address", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Export logs", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Cancel", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "ጌጋ ጸብጻብ ልኣኸ", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "email": "Email", + "@email": { + "description": "Email field label" + }, + "verify": "Verify", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Invalid email address", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Please enter a valid email address.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "በጃኻ ተጸበ...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "ቃለ-ምስጢር ኣረጋግጽ", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "ግጉይ ቃለ-ምስጢር", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Please try again", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Enter password", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Enter your password", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Active sessions", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "ዉዉኡ", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Something went wrong, please try again", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "This will log you out of this device!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "This will log you out of the following device:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Terminate session?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Terminate", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "This device", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Create account", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Weak", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Moderate", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Strong", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Delete account", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "We'll be sorry to see you go. Are you facing some issue?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Yes, send feedback", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "No, delete account", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Please authenticate to initiate account deletion", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Confirm account deletion", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "delete": "Delete", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Create new account", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Password", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Confirm password", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Password strength: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "How did you hear about Ente? (optional)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "ተጠቀምትና ኣይንከታተልን ኢና። ኣበይ ከም ዝረኸብካና እንተ ትነግረና ሓጋዚ እዩ፦", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "I agree to the terms of service and privacy policy", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Terms", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Privacy Policy", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Encryption", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Log in", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "እንኳዕ ብደሓን ተመለስካ!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "By clicking log in, I agree to the terms of service and privacy policy", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "No internet connection", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Please check your internet connection and try again.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Verification failed, please try again", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Recreate password", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "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).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "ምሕዋይ መፍትሕ ተጠቐም", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Forgot password", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Change email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Verify email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "We have sent a mail to {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "To reset your password, please verify your email first.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Please check your inbox (and spam) to complete verification", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Tap to enter code", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Send email", + "resendEmail": "Resend email", + "@resendEmail": { + "description": "Resend email button label" + }, + "tryAgain": "Try again", + "@tryAgain": { + "description": "Try again button label" + }, + "recoverAccount": "Recover account", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Set password", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Change password", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Reset password", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Encryption keys", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Enter a password we can use to encrypt your data", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Enter a new password we can use to encrypt your data", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "We don't store this password, so if you forget, we cannot decrypt your data", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "How it works", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Generating encryption keys...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Password changed successfully", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Sign out from other devices", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "If you think someone might know your password, you can force all other devices using your account to sign out.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "ካብ ካልኦት መሳርሒታት ኣውጽኡኒ።", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "ኣውጽኡኒ።", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "ናይ ምስጢራዊ ቁልፊ ዪፍጠር...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Continue", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Insecure device", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Recovery key copied to clipboard", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "ምሕዋይ መፍትሕ", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "If you forget your password, the only way you can recover your data is with this key.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "We don't store this key, please save this 24 word key in a safe place.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Do this later", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Save key", + "@saveKey": { + "description": "Save key button label" + }, + "noRecoveryKeyTitle": "No recovery key?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Two-factor authentication", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Enter the 6-digit code from\nyour authenticator app", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Lost device?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Enter your recovery key", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Recover", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "ወጸ...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "areYouSureYouWantToLogout": "Are you sure you want to logout?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Yes, logout", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Please authenticate to view your secrets", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "confirm": "Confirm", + "@confirm": { + "description": "Confirm button label" + }, + "androidBiometricHint": "Verify identity", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Not recognized. Try again.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Success", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancel", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Authentication required", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometric required", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Device credentials required", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Device credentials required", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Go to settings", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometric authentication is not set up on your device. Go to 'Settings > Security' to add biometric authentication.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "This email is already in use", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Email changed to {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Authentication failed, please try again", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Authentication successful!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "ክፍለ ግዜኡ ኣኺሉ።", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Incorrect recovery key", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "The recovery key you entered is incorrect", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Two-factor authentication successfully reset", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Your verification code has expired", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Incorrect code", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, the code you've entered is incorrect", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_tr.arb b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb index 2b7b873a60..2a0ef1e277 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_tr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "Ente'ye bağlanılamıyor, lütfen ağ ayarlarınızı kontrol edin ve hata devam ederse desteğe başvurun." -} + "networkHostLookUpErr": "Ente'ye bağlanılamıyor, lütfen ağ ayarlarınızı kontrol edin ve hata devam ederse desteğe başvurun.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Ente'ye bağlanılamıyor, lütfen daha sonra tekrar deneyin. Hata devam ederse, lütfen desteğe başvurun.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Bir şeyler ters gitmiş gibi görünüyor. Lütfen bir süre sonra tekrar deneyin. Hata devam ederse, lütfen destek ekibimizle iletişime geçin.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Hata", + "@error": { + "description": "Generic error title" + }, + "ok": "Tamam", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "SSS", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Destek ekibiyle iletişime geçin", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Kayıtlarınızı e-postayla gönderin", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Lütfen kayıtları şu adrese gönderin\n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "E-posta adresini kopyala", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Kayıtları dışa aktar", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "İptal Et", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Hata bildirin", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Bağlandı: {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Kaydet", + "@save": { + "description": "Label for save button" + }, + "send": "Gönder", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Bunu belleğinize mi kaydedeceksiniz (İndirilenler klasörü varsayılandır) yoksa diğer uygulamalara mı göndereceksiniz?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Bunu belleğinize kaydetmek ister misiniz? (İndirilenler klasörü varsayılandır)", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "Yeni e-posta adresinizi girin", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "E-Posta", + "@email": { + "description": "Email field label" + }, + "verify": "Doğrula", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Geçersiz e-posta adresi", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Lütfen geçerli bir e-posta adresi girin.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Lütfen bekleyin...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Şifreyi doğrulayın", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Yanlış şifre", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Lütfen tekrar deneyin", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Şifreyi girin", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Parolanızı girin", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Aktif oturumlar", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Hay aksi", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Bir şeyler ters gitti, lütfen tekrar deneyin", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Bu sizin bu cihazdaki oturumunuzu kapatacaktır!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Bu, aşağıdaki cihazdan çıkış yapmanızı sağlayacaktır:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Oturumu sonlandır?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Sonlandır", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Bu cihaz", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Hesap oluştur", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Zayıf", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Orta", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Güçlü", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Hesabı sil", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Sizin gittiğinizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Evet, geri bildirimi gönder", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Hayır, hesabı sil", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Hesap silme işlemini yapabilmek için lütfen kimliğinizi doğrulayın", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Hesap silme işlemini onayla", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Kullandığınız Ente uygulamaları varsa bu hesap diğer Ente uygulamalarıyla bağlantılıdır.\n\nTüm Ente uygulamalarına yüklediğiniz veriler ve hesabınız kalıcı olarak silinecektir.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Sil", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Yeni hesap oluşturun", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Şifre", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Şifreyi onayla", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Şifre gücü: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Ente'yi nereden duydunuz? (opsiyonel)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Biz uygulama kurulumlarını takip etmiyoruz. Bizi nereden duyduğunuzdan bahsetmeniz bize çok yardımcı olacak!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Kullanım şartlarını ve gizlilik politikasını kabul ediyorum", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Şartlar", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Gizlilik Politikası", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Eğer şifremi kaybedersem, verilerim uçtan uca şifrelendiğinden verilerimi kaybedebileceğimi anladım.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Şifreleme", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Giriş yapın", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Tekrar hoş geldiniz!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Giriş yaparak, kullanım şartları nı ve gizlilik politikası nı onaylıyorum", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "İnternet bağlantısı yok", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Lütfen internet bağlantınızı kontrol edin ve yeniden deneyin.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Doğrulama başarısız oldu, lütfen tekrar deneyin", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Şifreyi yeniden oluştur", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Mevcut cihaz şifrenizi doğrulayacak kadar güçlü değil, ancak tüm cihazlarla çalışacak şekilde yeniden oluşturabiliriz.\n\nLütfen kurtarma anahtarınızı kullanarak giriş yapın ve şifrenizi yeniden oluşturun (isterseniz aynı şifreyi tekrar kullanabilirsiniz).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Kurtarma anahtarını kullan", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Şifremi unuttum", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "E-posta adresini değiştir", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "E-posta adresini doğrulayın", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "{email} adresine bir posta gönderdik", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Şifrenizi sıfırlamak için lütfen önce e-postanızı doğrulayın.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Doğrulamayı tamamlamak için lütfen gelen kutunuzu (ve spam kutunuzu) kontrol edin", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Kodu girmek için dokunun", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "E-posta gönder", + "resendEmail": "E-postayı yeniden gönder", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Doğrulama hala bekliyor", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Oturum süresi doldu", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Oturum süreniz doldu. Tekrar giriş yapın.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Geçiş anahtarı doğrulaması", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Doğrulama bekleniyor...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Tekrar deneyin", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Durumu kontrol et", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "TOTP ile giriş yap", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Hesap kurtarma", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Şifre belirleyin", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Şifreyi değiştirin", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Şifreyi sıfırlayın", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Şifreleme anahtarları", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Verilerinizi şifrelemek için kullanabileceğimiz bir şifre girin", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Verilerinizi şifrelemek için kullanabileceğimiz yeni bir şifre girin", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Bu şifreyi saklamıyoruz, bu nedenle unutursanız, verilerinizin şifresini çözemeyiz", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Nasıl çalışır", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Şifreleme anahtarları oluşturuluyor...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Şifre başarıyla değiştirildi", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Diğer cihazlardan çıkış yap", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Eğer başka birisinin parolanızı bildiğini düşünüyorsanız, diğer tüm cihazları hesabınızdan çıkışa zorlayabilirsiniz.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Diğer cihazlardan çıkış yap", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Çıkış yapma", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Şifreleme anahtarları üretiliyor...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Devam et", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Güvenli olmayan cihaz", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Üzgünüz, bu cihazda güvenli anahtarlar oluşturamadık.\n\nlütfen farklı bir cihazdan kaydolun.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Kurtarma anahtarı panoya kopyalandı", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Kurtarma Anahtarı", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Eğer şifrenizi unutursanız, verilerinizi kurtarabileceğiniz tek yol bu anahtardır.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Biz bu anahtarı saklamıyoruz, lütfen. bu 24 kelimelik anahtarı güvenli bir yerde saklayın.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Bunu daha sonra yap", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Anahtarı kaydet", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Kurtarma anahtarı İndirilenler klasörüne kaydedildi!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Kurtarma anahtarınız yok mu?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "İki faktörlü kimlik doğrulama", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Kimlik doğrulayıcı uygulamanızdaki 6 haneli doğrulama kodunu girin", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Cihazınızı mı kaybettiniz?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Kurtarma anahtarınızı girin", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Kurtar", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Çıkış yapılıyor...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Hemen", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Uygulama kilidi", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Otomatik Kilit", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Sistem kilidi bulunamadı", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Cihaz kilidini etkinleştirmek için, lütfen cihaz şifresini veya ekran kilidini ayarlayın.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Cihazınızın varsayılan kilit ekranı ile PIN veya parola içeren özel bir kilit ekranı arasında seçim yapın.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Cihaz kilidi", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin kilidi", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Uygulamayı arka plana attıktan sonra kilitlendiği süre", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "İçeriği gizle", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Uygulama değiştiricide bulunan uygulama içeriğini gizler ve ekran görüntülerini devre dışı bırakır", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Uygulama değiştiricideki uygulama içeriğini gizler", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Çok fazla hatalı deneme", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Açmak için dokun", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Çıkış yapmak istediğinize emin misiniz?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Evet, çıkış yap", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Kodlarınızı görmek için lütfen kimlik doğrulaması yapın", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Sonraki", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Yeni şifre belirle", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "PIN Girin", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Yeni PIN belirleyin", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Doğrula", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Şifrenizi tekrar girin", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "PIN'inizi tekrar girin", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Kimliği doğrula", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Tanınmadı. Tekrar deneyin.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Başarılı", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "İptal et", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Kimlik doğrulaması gerekli", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biyometrik gerekli", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Cihaz kimlik bilgileri gerekli", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Cihaz kimlik bilgileri gerekmekte", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Ayarlara git", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biyometrik kimlik doğrulama cihazınızda ayarlanmamış. Biyometrik kimlik doğrulama eklemek için 'Ayarlar > Güvenlik' bölümüne gidin.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biyometrik kimlik doğrulama devre dışı. Etkinleştirmek için lütfen ekranınızı kilitleyin ve kilidini açın.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Tamam", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "E-posta zaten kayıtlı.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "E-posta kayıtlı değil.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Bu e-posta zaten kullanılıyor", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "E-posta {newEmail} olarak değiştirildi", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Kimlik doğrulama başarısız oldu, lütfen tekrar deneyin", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Kimlik doğrulama başarılı!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Oturum süresi doldu", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Yanlış kurtarma kodu", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Girdiğiniz kurtarma kodu yanlış", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "İki faktörlü kimlik doğrulama başarıyla sıfırlandı", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Doğrulama kodunuzun süresi doldu", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Yanlış kod", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Üzgünüz, girdiğiniz kod yanlış", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_uk.arb b/mobile/packages/strings/lib/l10n/arb/strings_uk.arb new file mode 100644 index 0000000000..38a754283c --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_uk.arb @@ -0,0 +1,692 @@ +{ + "networkHostLookUpErr": "Не вдалося приєднатися до Ente. Будь ласка, перевірте налаштування мережі. Зверніться до нашої команди підтримки, якщо помилка залишиться.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Не вдалося приєднатися до Ente. Будь ласка, спробуйте ще раз через деякий час. Якщо помилка не зникне, зв'яжіться з нашою командою підтримки.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Схоже, що щось пішло не так. Будь ласка, спробуйте ще раз через деякий час. Якщо помилка не зникне, зв'яжіться з нашою командою підтримки.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Помилка", + "@error": { + "description": "Generic error title" + }, + "ok": "Ок", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Часті питання", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Звернутися до служби підтримки", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Відправте ваші журнали електронною поштою", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Будь ласка, надішліть журнали до електронної пошти {toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Копіювати електронну адресу", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Експортувати журнал", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Скасувати", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Повідомити про помилку", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Приєднано до {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Зберегти", + "@save": { + "description": "Label for save button" + }, + "send": "Надіслати", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Чи хочете ви зберегти це до свого сховища (типово тека Downloads), чи надіслати його в інші застосунки?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Чи хочете Ви зберегти це до свого сховища (тека Downloads за замовчуванням)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Адреса електронної пошти", + "@email": { + "description": "Email field label" + }, + "verify": "Перевірити", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Хибна адреса електронної пошти", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Введіть дійсну адресу електронної пошти.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Будь ласка, зачекайте...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Підтвердження пароля", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Невірний пароль", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Будь ласка, спробуйте ще раз", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Введіть пароль", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Введіть свій пароль", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Активні сеанси", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "От халепа", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Щось пішло не так, спробуйте, будь ласка, знову", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Це призведе до виходу на цьому пристрої!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Це призведе до виходу на наступному пристрої:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Припинити сеанс?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Припинити", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Цей пристрій", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Створити обліковий запис", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Слабкий", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Помірний", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Надійний", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Видалити обліковий запис", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Нам дуже шкода, що Ви залишаєте нас. Чи Ви зіткнулися з якоюсь проблемою?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Так, надіслати відгук", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Ні, видаліть мій обліковий запис", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Будь ласка, авторизуйтесь, щоб розпочати видалення облікового запису", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Підтвердіть видалення облікового запису", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Цей обліковий запис є зв'язаним з іншими програмами Ente, якщо ви використовуєте якісь з них.\n\nВаші завантажені дані у всіх програмах Ente будуть заплановані до видалення, а обліковий запис буде видалено назавжди.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Видалити", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Створити новий обліковий запис", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Пароль", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Підтвердити пароль", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Сила пароля: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Як ви дізналися про Ente? (опціонально)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Ми не відстежуємо встановлення застосунків. Але, якщо ви скажете нам, де ви нас знайшли, це допоможе!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Я приймаю умови використання і політику конфіденційності", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Умови", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Політика конфіденційності", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Я розумію, що якщо я втрачу свій пароль, я можу втратити свої дані, тому що вони є захищені наскрізним шифруванням.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Шифрування", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Увійти", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "З поверненням!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Натискаючи «Увійти», я приймаю умови використання і політику конфіденційності", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Немає підключення до Інтернету", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Будь ласка, перевірте підключення до Інтернету та спробуйте ще раз.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Перевірка не вдалася, спробуйте ще", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Повторно створити пароль", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Поточний пристрій не є достатньо потужним для підтвердження пароля, але ми можемо відновити його таким чином, щоб він працював на всіх пристроях.\n\nБудь ласка, увійдіть за допомогою вашого ключа відновлення і відновіть ваш пароль (ви можете знову використати той самий пароль, якщо бажаєте).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Застосувати ключ відновлення", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Нагадати пароль", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Змінити адресу електронної пошти", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Підтвердити електронну адресу", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Ми надіслали листа на адресу електронної пошти {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Щоб скинути пароль, будь ласка, спочатку підтвердіть адресу своєї електронної пошти.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Будь ласка, перевірте вашу скриньку електронної пошти (та спам), щоб завершити перевірку", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Натисніть, щоб ввести код", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Надіслати електронного листа", + "resendEmail": "Повторно надіслати лист на електронну пошту", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Підтвердження все ще в процесі", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Час сеансу минув", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Термін дії вашого сеансу завершився. Будь ласка, увійдіть знову.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Перевірка секретного ключа", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Очікується підтвердження...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Спробуйте ще раз", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Перевірити стан", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Увійти за допомогою TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Відновити обліковий запис", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Встановити пароль", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Змінити пароль", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Скинути пароль", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Ключі шифрування", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Введіть пароль, який ми зможемо використати для шифрування ваших даних", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Введіть новий пароль, який ми зможемо використати для шифрування ваших даних", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Ми не зберігаємо цей пароль, тому, якщо ви його забудете, ми не зможемо розшифрувати Ваші дані", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Як це працює", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Створення ключів шифрування...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Пароль успішно змінено", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Вийти на інших пристроях", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Якщо ви думаєте, що хтось може знати ваш пароль, ви можете примусити всі інші пристрої, які використовують ваш обліковий запис, вийти з нього.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Вийти на інших пристроях", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Не виходити", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Створення ключів шифрування...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Продовжити", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Незахищений пристрій", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "На жаль, нам не вдалося згенерувати захищені ключі на цьому пристрої.\n\nБудь ласка, увійдіть з іншого пристрою.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Ключ відновлення скопійований в буфер обміну", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Ключ відновлення", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Якщо ви забудете свій пароль, то єдиний спосіб відновити ваші дані – за допомогою цього ключа.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Ми не зберігаємо цей ключ, будь ласка, збережіть цей ключ з 24 слів в надійному місці.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Зробити це пізніше", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Зберегти ключ", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Ключ відновлення збережений у теці Downloads!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Немає ключа відновлення?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Двоетапна автентифікація", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Введіть нижче шестизначний код із застосунку для автентифікації", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Загубили пристрій?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Введіть ваш ключ відновлення", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Відновлення", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Вихід із системи...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Негайно", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Блокування", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Автоблокування", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Не знайдено системного блокування", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Для увімкнення блокування програми, будь ласка, налаштуйте пароль пристрою або блокування екрана в системних налаштуваннях.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Виберіть між типовим екраном блокування вашого пристрою та власним екраном блокування з PIN-кодом або паролем.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Блокування пристрою", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "PIN-код", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Час, через який застосунок буде заблоковано після розміщення у фоновому режимі", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Приховати вміст", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Приховує вміст програми у перемикачі застосунків і вимикає знімки екрану", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Приховує вміст у перемикачі застосунків", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Завелика кількість невірних спроб", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Доторкніться, щоб розблокувати", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Ви впевнені, що хочете вийти з системи?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Так, вийти з системи", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Будь ласка, пройдіть автентифікацію, щоб переглянути ваші секретні коди", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Наступний", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Встановити новий пароль", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Введіть PIN-код", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Встановити новий PIN-код", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Підтвердити", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Введіть пароль ще раз", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Введіть PIN-код ще раз", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Підтвердити ідентифікацію", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Не розпізнано. Спробуйте ще раз.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Успіх", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Скасувати", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Необхідна автентифікація", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Потрібна біометрична автентифікація", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Необхідні облікові дані пристрою", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Необхідні облікові дані пристрою", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Перейти до налаштувань", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Біометрична автентифікація не налаштована на вашому пристрої. Перейдіть в «Налаштування > Безпека», щоб додати біометричну автентифікацію.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Біометрична автентифікація вимкнена. Будь ласка, заблокуйте і розблокуйте свій екран, щоб увімкнути її.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "thisEmailIsAlreadyInUse": "Ця адреса електронної пошти вже використовується", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Адресу електронної пошти змінено на {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Автентифікація не пройдена. Будь ласка, спробуйте ще раз", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Автентифікацію виконано!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Час сеансу минув", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Неправильний ключ відновлення", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Ви ввели неправильний ключ відновлення", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Двоетапна автентифікація успішно скинута", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Час дії коду підтвердження минув", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Невірний код", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Вибачте, але введений вами код є невірним", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_vi.arb b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb index 32f5e63fd4..2f98a1ac2d 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_vi.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb @@ -1,3 +1,700 @@ { - "networkHostLookUpErr": "Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ." -} + "networkHostLookUpErr": "Không thể kết nối đến Ente, vui lòng kiểm tra lại kết nối mạng. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "Không thể kết nối đến Ente, vui lòng thử lại sau. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "Lỗi", + "@error": { + "description": "Generic error title" + }, + "ok": "Đồng ý", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "Câu hỏi thường gặp", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "Liên hệ hỗ trợ", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "Gửi email nhật ký của bạn", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "Vui lòng gửi nhật ký đến \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "Sao chép địa chỉ email", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "Xuất nhật ký", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "Hủy", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "Báo cáo lỗi", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "Đã kết nối đến", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "Lưu", + "@save": { + "description": "Label for save button" + }, + "send": "Gửi", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "Bạn có muốn lưu vào bộ nhớ (Mặc định lưu vào thư mục Tải về) hoặc chuyển qua ứng dụng khác?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "Bạn có muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "Thư điện tử", + "@email": { + "description": "Email field label" + }, + "verify": "Xác minh", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "Địa chỉ email không hợp lệ", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "Xin vui lòng nhập một địa chỉ email hợp lệ.", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "Vui lòng chờ...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "Xác nhận mật khẩu", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "Mật khẩu không đúng", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "Vui lòng thử lại", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "Nhập mật khẩu", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "Nhập mật khẩu của bạn", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "Các phiên làm việc hiện tại", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "Rất tiếc", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "Phát hiện có lỗi, xin thử lại", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "Thao tác này sẽ đăng xuất bạn khỏi thiết bị này!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "Thao tác này sẽ đăng xuất bạn khỏi thiết bị sau:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "Kết thúc phiên?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "Kết thúc", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "Thiết bị này", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "Tạo tài khoản", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "Yếu", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "Trung bình", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "Mạnh", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "Xoá tài khoản", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "Chúng tôi sẽ rất tiếc khi thấy bạn đi. Bạn đang phải đối mặt với một số vấn đề?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "Có, gửi phản hồi", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "Không, xóa tài khoản", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "Vui lòng xác thực để bắt đầu xóa tài khoản", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "Xác nhận xóa tài khoản", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "Xóa", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "Tạo tài khoản mới", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "Mật khẩu", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "Xác nhận mật khẩu", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "Độ mạnh mật khẩu: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "Bạn biết đến Ente bằng cách nào? (không bắt buộc)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "Tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "Điều khoản", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "Chính sách bảo mật", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "Tôi hiểu rằng việc mất mật khẩu có thể đồng nghĩa với việc mất dữ liệu của tôi vì dữ liệu của tôi được mã hóa hai đầu.", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "Mã hóa", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "Đăng nhập", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "Chào mừng trở lại!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "Bằng cách nhấp vào đăng nhập, tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "Không có kết nối Internet", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "Vui lòng kiểm tra kết nối internet của bạn và thử lại.", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "Mã xác nhận thất bại. Vui lòng thử lại", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "Tạo lại mật khẩu", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn nhưng chúng tôi có thể tạo lại mật khẩu theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dụng lại cùng một mật khẩu nếu muốn).", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "Dùng khóa khôi phục", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "Quên mật khẩu", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "Thay đổi email", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "Xác nhận địa chỉ Email", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "Chúng tôi đã gửi thư đến {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "Để đặt lại mật khẩu, vui lòng xác minh email của bạn trước.", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "Vui lòng kiểm tra hộp thư đến (và thư rác) của bạn để hoàn tất xác minh", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "Chạm để nhập mã", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "Gửi email", + "resendEmail": "Gửi lại email", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "Đang chờ xác thực", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "Phiên làm việc hết hạn", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "Phiên làm việc hết hạn. Vui lòng đăng nhập lại.", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "Xác minh mã khóa", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "Đang chờ xác thực", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "Thử lại", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "Kiểm tra trạng thái", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "Đăng nhập bằng TOTP", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "Khôi phục tài khoản", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "Đặt mật khẩu", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "Thay đổi mật khẩu", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "Đặt lại mật khẩu", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "Khóa mã hóa", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "Nhập mật khẩu mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "Nhập một mật khẩu mới mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "Chúng tôi không lưu trữ mật khẩu này, vì vậy nếu bạn quên, chúng tôi không thể giải mã dữ liệu của bạn", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "Cách thức hoạt động", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "Đang tạo khóa mã hóa...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "Thay đổi mật khẩu thành công", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "Đăng xuất khỏi các thiết bị khác", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "Nếu bạn cho rằng ai đó có thể biết mật khẩu của mình, bạn có thể buộc đăng xuất tất cả các thiết bị khác đang sử dụng tài khoản của mình.", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "Đăng xuất khỏi các thiết bị khác", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "Không được đăng xuất", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "Đang tạo khóa mã hóa...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "Tiếp tục", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "Thiết bị không an toàn", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Rất tiếc, chúng tôi không thể tạo khóa bảo mật trên thiết bị này.\n\nvui lòng đăng ký từ một thiết bị khác.", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "Đã sao chép khóa khôi phục vào bộ nhớ tạm", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "Khóa khôi phục", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "Nếu bạn quên mật khẩu, cách duy nhất bạn có thể khôi phục dữ liệu của mình là sử dụng khóa này.", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "Để sau", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "Lưu khóa", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "Đã lưu khoá dự phòng vào thư mục Tải về!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "Không có khóa khôi phục?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "Xác thực hai yếu tố", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "Mất thiết bị?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "Nhập khóa khôi phục của bạn", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "Khôi phục", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "Đang đăng xuất...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Tức thì", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "Khóa ứng dụng", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Tự động khóa", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "Không thấy khoá hệ thống", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Chọn giữa màn hình khoá mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Khóa thiết bị", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Mã PIN", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Thời gian ứng dụng tự khoá sau khi ở trạng thái nền", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Ẩn nội dung", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Ẩn nội dung khi chuyển ứng dụng", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Quá nhiều lần thử không chính xác", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Nhấn để mở khóa", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Bạn có chắc chắn muốn đăng xuất?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Có, đăng xuất", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Vui lòng xác thực để xem bí mật của bạn", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Tiếp theo", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Đặt lại mật khẩu", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Nhập mã PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Đổi mã PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Xác nhận", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Nhập lại mật khẩu", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Nhập lại mã PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Xác định danh tính", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Không nhận dạng được. Vui lòng thử lại.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Thành công", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Hủy bỏ", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Yêu cầu xác thực", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Yêu cầu sinh trắc học", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Yêu cầu thông tin xác thực thiết bị", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Yêu cầu thông tin xác thực thiết bị", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Chuyển đến cài đặt", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi tới 'Cài đặt > Bảo mật' để thêm xác thực sinh trắc học.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Xác thực sinh trắc học bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt nó.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "Đồng ý", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Email đã được đăng kí.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Email chưa được đăng kí.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "Email này đã được sử dụng", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Thay đổi email thành {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Xác thực lỗi, vui lòng thử lại", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Xác thực thành công!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Phiên làm việc đã hết hạn", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Khóa khôi phục không chính xác", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "Khóa khôi phục bạn đã nhập không chính xác", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Xác thực hai bước được khôi phục thành công", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "Mã xác minh của bạn đã hết hạn", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Mã không chính xác", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Xin lỗi, mã bạn đã nhập không chính xác", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb index 532b55dd09..d55445222b 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_zh.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb @@ -1,3 +1,700 @@ { - "networkHostLookUpErr": "无法连接到 Ente,请检查您的网络设置,如果错误仍然存在,请联系支持。" -} + "networkHostLookUpErr": "无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "错误", + "@error": { + "description": "Generic error title" + }, + "ok": "确定", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "常见问题", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "联系支持", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "通过电子邮件发送您的日志", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "请将日志发送至 \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "复制电子邮件地址", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "导出日志", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "取消", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "报告错误", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "已连接至 {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "保存", + "@save": { + "description": "Label for save button" + }, + "send": "发送", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "email": "电子邮件地址", + "@email": { + "description": "Email field label" + }, + "verify": "验证", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "无效的电子邮件地址", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "请输入一个有效的电子邮件地址。", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "请稍候...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "验证密码", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "密码错误", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "请重试", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "输入密码", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "输入您的密码", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "已登录的设备", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "哎呀", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "出了点问题,请重试", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "这将使您登出该设备!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "这将使您登出以下设备:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "是否终止会话?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "终止", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "此设备", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "创建账户", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "弱", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "中", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "强", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "删除账户", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "我们很抱歉看到您离开。您面临一些问题?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "是,发送反馈", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "否,删除账户", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "请进行身份验证以启动账户删除", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "确认删除账户", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "删除", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "创建新账号", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "密码", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "请确认密码", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "密码强度: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "您是怎么知道 Ente 的?(可选)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "我同意 服务条款隐私政策", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "服务条款", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "隐私政策", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "加密", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "登录", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "欢迎回来!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "点击登录后,我同意 服务条款隐私政策", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "无互联网连接", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "请检查您的互联网连接,然后重试。", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "验证失败,请再试一次", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "重新创建密码", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "使用恢复密钥", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "忘记密码", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "修改邮箱", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "验证电子邮件", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "我们已经发送邮件到 {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "要重置您的密码,请先验证您的电子邮件。", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "点击以输入代码", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "发送电子邮件", + "resendEmail": "重新发送电子邮件", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "仍需验证", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "会话已过期", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "您的会话已过期。请重新登录。", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "通行密钥验证", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "等待验证...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "请再试一次", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "检查状态", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "使用 TOTP 登录", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "恢复账户", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "设置密码", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "修改密码", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "重置密码", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "加密密钥", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "输入我们可以用来加密您的数据的密码", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "输入我们可以用来加密您的数据的新密码", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "我们不储存这个密码,所以如果忘记, 我们不能解密您的数据", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "工作原理", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "正在生成加密密钥...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "密码修改成功", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "从其他设备登出", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "登出其他设备", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "不要登出", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "正在生成加密密钥...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "继续", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "设备不安全", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "恢复密钥已复制到剪贴板", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "恢复密钥", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "稍后再做", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "保存密钥", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "恢复密钥已保存在下载文件夹中!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "没有恢复密钥吗?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "两步验证", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "丢失了设备吗?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "输入您的恢复密钥", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "恢复", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "正在登出...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "立即", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "应用锁", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "自动锁定", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "未找到系统锁", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "要启用设备锁,请在系统设置中设置设备密码或屏幕锁。", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "设备锁", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin 锁定", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "应用程序进入后台后锁定的时间", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "隐藏内容", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "在应用切换器中隐藏应用内容并禁用屏幕截图", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "在应用切换器中隐藏应用内容", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "错误的尝试次数过多", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "点击解锁", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "您确定要登出吗?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "是的,登出", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "请进行身份验证以查看您的密钥", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "下一步", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "设置新密码", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "输入 PIN 码", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "设置新 PIN 码", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "确认", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "再次输入密码", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "再次输入 PIN 码", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "验证身份", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "未能识别,请重试。", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "成功", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "取消", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "需要进行身份验证", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "需要进行生物识别认证", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "需要设备凭据", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "需要设备凭据", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "前往设置", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "好", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "电子邮件地址已被注册。", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "电子邮件地址未注册。", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "该电子邮件已被使用", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "电子邮件已更改为 {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "认证失败,请重试", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "认证成功!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "会话已过期", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "恢复密钥不正确", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "您输入的恢复密钥不正确", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "两步验证已成功重置", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "您的验证码已过期", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "验证码错误", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您输入的验证码不正确", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb new file mode 100644 index 0000000000..ac625670ad --- /dev/null +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb @@ -0,0 +1,704 @@ +{ + "networkHostLookUpErr": "无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "错误", + "@error": { + "description": "Generic error title" + }, + "ok": "确定", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "常见问题", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "联系支持", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "通过电子邮件发送您的日志", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "请将日志发送至 \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "复制电子邮件地址", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "导出日志", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "取消", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "报告错误", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "已连接至 {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "保存", + "@save": { + "description": "Label for save button" + }, + "send": "发送", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "请输入您的新电子邮件地址", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "电子邮件地址", + "@email": { + "description": "Email field label" + }, + "verify": "验证", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "无效的电子邮件地址", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "请输入一个有效的电子邮件地址。", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "请稍候...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "验证密码", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "密码错误", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "请重试", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "输入密码", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "输入您的密码", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "已登录的设备", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "哎呀", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "出了点问题,请重试", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "这将使您登出该设备!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "这将使您登出以下设备:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "是否终止会话?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "终止", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "此设备", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "创建账户", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "弱", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "中", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "强", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "删除账户", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "我们很抱歉看到您离开。您面临一些问题?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "是,发送反馈", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "否,删除账户", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "请进行身份验证以启动账户删除", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "确认删除账户", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "删除", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "创建新账号", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "密码", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "请确认密码", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "密码强度: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "您是怎么知道 Ente 的?(可选)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "我同意 服务条款隐私政策", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "服务条款", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "隐私政策", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "加密", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "登录", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "欢迎回来!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "点击登录后,我同意 服务条款隐私政策", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "无互联网连接", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "请检查您的互联网连接,然后重试。", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "验证失败,请再试一次", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "重新创建密码", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "使用恢复密钥", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "忘记密码", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "修改邮箱", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "验证电子邮件", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "我们已经发送邮件到 {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "要重置您的密码,请先验证您的电子邮件。", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "点击以输入代码", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "发送电子邮件", + "resendEmail": "重新发送电子邮件", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "仍需验证", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "会话已过期", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "您的会话已过期。请重新登录。", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "通行密钥验证", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "等待验证...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "请再试一次", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "检查状态", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "使用 TOTP 登录", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "恢复账户", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "设置密码", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "修改密码", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "重置密码", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "加密密钥", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "输入我们可以用来加密您的数据的密码", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "输入我们可以用来加密您的数据的新密码", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "我们不储存这个密码,所以如果忘记, 我们不能解密您的数据", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "工作原理", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "正在生成加密密钥...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "密码修改成功", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "从其他设备登出", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "登出其他设备", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "不要登出", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "正在生成加密密钥...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "继续", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "设备不安全", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "恢复密钥已复制到剪贴板", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "恢复密钥", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "稍后再做", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "保存密钥", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "恢复密钥已保存在下载文件夹中!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "没有恢复密钥吗?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "两步验证", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "丢失了设备吗?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "输入您的恢复密钥", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "恢复", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "正在登出...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "立即", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "应用锁", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "自动锁定", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "未找到系统锁", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "要启用设备锁,请在系统设置中设置设备密码或屏幕锁。", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "设备锁", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin 锁定", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "应用程序进入后台后锁定的时间", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "隐藏内容", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "在应用切换器中隐藏应用内容并禁用屏幕截图", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "在应用切换器中隐藏应用内容", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "错误的尝试次数过多", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "点击解锁", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "您确定要登出吗?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "是的,登出", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "请进行身份验证以查看您的密钥", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "下一步", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "设置新密码", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "输入 PIN 码", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "设置新 PIN 码", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "确认", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "再次输入密码", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "再次输入 PIN 码", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "验证身份", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "未能识别,请重试。", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "成功", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "取消", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "需要进行身份验证", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "需要进行生物识别认证", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "需要设备凭据", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "需要设备凭据", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "前往设置", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "好", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "电子邮件地址已被注册。", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "电子邮件地址未注册。", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "该电子邮件已被使用", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "电子邮件已更改为 {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "认证失败,请重试", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "认证成功!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "会话已过期", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "恢复密钥不正确", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "您输入的恢复密钥不正确", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "两步验证已成功重置", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "您的验证码已过期", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "验证码错误", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您输入的验证码不正确", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb index 55b53dac78..54a6df72a8 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb @@ -1,3 +1,704 @@ { - "networkHostLookUpErr": "無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存在,請聯絡支援。" -} + "networkHostLookUpErr": "無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存​​在,請聯絡支援。", + "@networkHostLookUpErr": { + "description": "Error message shown when the app cannot connect to Ente due to network host lookup failure" + }, + "networkConnectionRefusedErr": "無法連接到 Ente,請稍後重試。如果錯誤仍然存​​在,請聯絡支援人員。", + "@networkConnectionRefusedErr": { + "description": "Error message shown when the app cannot connect to Ente due to connection refused" + }, + "itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "看起來出了點問題。 請稍後重試。 如果錯誤仍然存在,請聯絡我們的支援團隊。", + "@itLooksLikeSomethingWentWrongPleaseRetryAfterSome": { + "description": "Generic error message for temporary issues" + }, + "error": "錯誤", + "@error": { + "description": "Generic error title" + }, + "ok": "確定", + "@ok": { + "description": "Generic OK button label" + }, + "faq": "常見問題", + "@faq": { + "description": "FAQ link label" + }, + "contactSupport": "聯絡支援", + "@contactSupport": { + "description": "Contact support button label" + }, + "emailYourLogs": "通過電子郵件傳送您的日誌", + "@emailYourLogs": { + "description": "Title for emailing logs dialog" + }, + "pleaseSendTheLogsTo": "請將日誌傳送至 \n{toEmail}", + "@pleaseSendTheLogsTo": { + "description": "Message asking user to send logs to email address", + "placeholders": { + "toEmail": { + "type": "String", + "description": "Email address to send logs to" + } + } + }, + "copyEmailAddress": "複製電子郵件地址", + "@copyEmailAddress": { + "description": "Button to copy email address to clipboard" + }, + "exportLogs": "匯出日誌", + "@exportLogs": { + "description": "Button to export logs" + }, + "cancel": "取消", + "@cancel": { + "description": "Cancel button label" + }, + "reportABug": "報告錯誤", + "@reportABug": { + "description": "Label for reporting a bug" + }, + "customEndpoint": "已連接至 {endpoint}", + "@customEndpoint": { + "description": "Text showing user is connected to a custom endpoint", + "placeholders": { + "endpoint": { + "type": "String", + "description": "URL of the custom endpoint" + } + } + }, + "save": "儲存", + "@save": { + "description": "Label for save button" + }, + "send": "傳送", + "@send": { + "description": "Label for send button" + }, + "saveOrSendDescription": "您想將其儲存到您的內建儲存(預設情況下為“下載”資料夾)還是將其傳送到其他APP?", + "@saveOrSendDescription": { + "description": "Description text asking user if they want to save to storage or share with other apps" + }, + "saveOnlyDescription": "您想將其儲存到您的內建儲存中(預設情況下為“下載”資料夾)嗎?", + "@saveOnlyDescription": { + "description": "Description text asking user if they want to save to storage (for platforms that don't support sharing)" + }, + "enterNewEmailHint": "輸入你的新電子郵件地址", + "@enterNewEmailHint": { + "description": "Hint text for entering new email address" + }, + "email": "電子郵件地址", + "@email": { + "description": "Email field label" + }, + "verify": "驗證", + "@verify": { + "description": "Verify button label" + }, + "invalidEmailTitle": "無效的電子郵件地址", + "@invalidEmailTitle": { + "description": "Title for invalid email error dialog" + }, + "invalidEmailMessage": "請輸入一個有效的電子郵件地址。", + "@invalidEmailMessage": { + "description": "Message for invalid email error dialog" + }, + "pleaseWait": "請稍候...", + "@pleaseWait": { + "description": "Please wait message" + }, + "verifyPassword": "驗證密碼", + "@verifyPassword": { + "description": "Verify password button label" + }, + "incorrectPasswordTitle": "密碼錯誤", + "@incorrectPasswordTitle": { + "description": "Title for incorrect password error" + }, + "pleaseTryAgain": "請重試", + "@pleaseTryAgain": { + "description": "Message asking user to try again" + }, + "enterPassword": "輸入密碼", + "@enterPassword": { + "description": "Enter password field label" + }, + "enterYourPasswordHint": "輸入您的密碼", + "@enterYourPasswordHint": { + "description": "Hint for password field" + }, + "activeSessions": "已登錄的裝置", + "@activeSessions": { + "description": "Title for active sessions page" + }, + "oops": "哎呀", + "@oops": { + "description": "Oops error title" + }, + "somethingWentWrongPleaseTryAgain": "出了點問題,請重試", + "@somethingWentWrongPleaseTryAgain": { + "description": "Generic error message" + }, + "thisWillLogYouOutOfThisDevice": "這將使您登出該裝置!", + "@thisWillLogYouOutOfThisDevice": { + "description": "Warning message for logging out of current device" + }, + "thisWillLogYouOutOfTheFollowingDevice": "這將使您登出以下裝置:", + "@thisWillLogYouOutOfTheFollowingDevice": { + "description": "Warning message for logging out of another device" + }, + "terminateSession": "是否終止工作階段?", + "@terminateSession": { + "description": "Title for terminate session dialog" + }, + "terminate": "終止", + "@terminate": { + "description": "Terminate button label" + }, + "thisDevice": "此裝置", + "@thisDevice": { + "description": "Label for current device" + }, + "createAccount": "建立帳戶", + "@createAccount": { + "description": "Create account button label" + }, + "weakStrength": "弱", + "@weakStrength": { + "description": "Weak password strength label" + }, + "moderateStrength": "中", + "@moderateStrength": { + "description": "Moderate password strength label" + }, + "strongStrength": "強", + "@strongStrength": { + "description": "Strong password strength label" + }, + "deleteAccount": "刪除帳戶", + "@deleteAccount": { + "description": "Delete account button label" + }, + "deleteAccountQuery": "我們很抱歉看到您要刪除帳戶。您似乎面臨著一些問題?", + "@deleteAccountQuery": { + "description": "Message asking user why they want to delete account" + }, + "yesSendFeedbackAction": "是,傳送回饋", + "@yesSendFeedbackAction": { + "description": "Button to confirm sending feedback" + }, + "noDeleteAccountAction": "否,刪除帳戶", + "@noDeleteAccountAction": { + "description": "Button to proceed with account deletion" + }, + "initiateAccountDeleteTitle": "請進行身份驗證以啟動帳戶刪除", + "@initiateAccountDeleteTitle": { + "description": "Title for authentication dialog before account deletion" + }, + "confirmAccountDeleteTitle": "確認刪除帳戶", + "@confirmAccountDeleteTitle": { + "description": "Title for account deletion confirmation dialog" + }, + "confirmAccountDeleteMessage": "如果您使用其他 Ente APP,該帳戶將會與其他APP連結。\n\n在所有 Ente APP中,您上傳的資料將被安排用於刪除,並且您的帳戶將被永久刪除。", + "@confirmAccountDeleteMessage": { + "description": "Message warning about account deletion consequences" + }, + "delete": "刪除", + "@delete": { + "description": "Delete button label" + }, + "createNewAccount": "建立新帳號", + "@createNewAccount": { + "description": "Create new account button label" + }, + "password": "密碼", + "@password": { + "description": "Password field label" + }, + "confirmPassword": "請確認密碼", + "@confirmPassword": { + "description": "Confirm password field label" + }, + "passwordStrength": "密碼強度: {passwordStrengthValue}", + "@passwordStrength": { + "description": "Text to indicate the password strength", + "placeholders": { + "passwordStrengthValue": { + "description": "The strength of the password as a string", + "type": "String", + "example": "Weak or Moderate or Strong" + } + } + }, + "hearUsWhereTitle": "您是怎麼知道 Ente 的?(可選)", + "@hearUsWhereTitle": { + "description": "Title asking how user heard about Ente" + }, + "hearUsExplanation": "我們不跟蹤APP安裝情況。如果您告訴我們您是在哪裡找到我們的,將會有所幫助!", + "@hearUsExplanation": { + "description": "Explanation for asking how user heard about Ente" + }, + "signUpTerms": "我同意 服務條款隱私政策", + "@signUpTerms": { + "description": "Terms agreement text for sign up" + }, + "termsOfServicesTitle": "服務條款", + "@termsOfServicesTitle": { + "description": "Terms of service title" + }, + "privacyPolicyTitle": "隱私政策", + "@privacyPolicyTitle": { + "description": "Privacy policy title" + }, + "ackPasswordLostWarning": "我明白,如果我遺失密碼,我可能會遺失我的資料,因為我的資料是 端到端加密的。", + "@ackPasswordLostWarning": { + "description": "Warning about password loss" + }, + "encryption": "加密", + "@encryption": { + "description": "Encryption label" + }, + "logInLabel": "登錄", + "@logInLabel": { + "description": "Log in button label" + }, + "welcomeBack": "歡迎回來!", + "@welcomeBack": { + "description": "Welcome back message" + }, + "loginTerms": "點選登錄後,我同意 服務條款隱私政策", + "@loginTerms": { + "description": "Terms agreement text for login" + }, + "noInternetConnection": "無網際網路連接", + "@noInternetConnection": { + "description": "No internet connection error message" + }, + "pleaseCheckYourInternetConnectionAndTryAgain": "請檢查您的網際網路連接,然後重試。", + "@pleaseCheckYourInternetConnectionAndTryAgain": { + "description": "Message asking user to check internet connection" + }, + "verificationFailedPleaseTryAgain": "驗證失敗,請再試一次", + "@verificationFailedPleaseTryAgain": { + "description": "Verification failed error message" + }, + "recreatePasswordTitle": "重新建立密碼", + "@recreatePasswordTitle": { + "description": "Title for recreate password dialog" + }, + "recreatePasswordBody": "目前裝置的功能不足以驗證您的密碼,但我們可以以適用於所有裝置的方式重新產生。\n\n請使用您的復原密鑰登錄並重新產生您的密碼(如果您願意,可以再次使用相同的密碼)。", + "@recreatePasswordBody": { + "description": "Body text for recreate password dialog" + }, + "useRecoveryKey": "使用復原密鑰", + "@useRecoveryKey": { + "description": "Use recovery key button label" + }, + "forgotPassword": "忘記密碼", + "@forgotPassword": { + "description": "Forgot password link label" + }, + "changeEmail": "修改信箱", + "@changeEmail": { + "description": "Change email button label" + }, + "verifyEmail": "驗證電子郵件", + "@verifyEmail": { + "description": "Verify email title" + }, + "weHaveSendEmailTo": "我們已經傳送郵件到 {email}", + "@weHaveSendEmailTo": { + "description": "Text to indicate that we have sent a mail to the user", + "placeholders": { + "email": { + "description": "The email address of the user", + "type": "String", + "example": "example@ente.io" + } + } + }, + "toResetVerifyEmail": "要重設您的密碼,請先驗證您的電子郵件。", + "@toResetVerifyEmail": { + "description": "Message asking user to verify email before password reset" + }, + "checkInboxAndSpamFolder": "請檢查您的收件箱 (或者是在您的“垃圾郵件”列表內) 以完成驗證", + "@checkInboxAndSpamFolder": { + "description": "Message asking user to check inbox and spam folder" + }, + "tapToEnterCode": "點選以輸入程式碼", + "@tapToEnterCode": { + "description": "Hint for entering verification code" + }, + "sendEmail": "傳送電子郵件", + "resendEmail": "重新傳送電子郵件", + "@resendEmail": { + "description": "Resend email button label" + }, + "passKeyPendingVerification": "仍需驗證", + "@passKeyPendingVerification": { + "description": "Message when passkey verification is pending" + }, + "loginSessionExpired": "工作階段已過期", + "@loginSessionExpired": { + "description": "Login session expired title" + }, + "loginSessionExpiredDetails": "您的工作階段已過期。請重新登錄。", + "@loginSessionExpiredDetails": { + "description": "Login session expired details" + }, + "passkeyAuthTitle": "通行金鑰驗證", + "@passkeyAuthTitle": { + "description": "Passkey authentication title" + }, + "waitingForVerification": "等待驗證...", + "@waitingForVerification": { + "description": "Waiting for verification message" + }, + "tryAgain": "請再試一次", + "@tryAgain": { + "description": "Try again button label" + }, + "checkStatus": "檢查狀態", + "@checkStatus": { + "description": "Check status button label" + }, + "loginWithTOTP": "使用 TOTP 登錄", + "@loginWithTOTP": { + "description": "Login with TOTP button label" + }, + "recoverAccount": "恢復帳戶", + "@recoverAccount": { + "description": "Recover account button label" + }, + "setPasswordTitle": "設定密碼", + "@setPasswordTitle": { + "description": "Set password title" + }, + "changePasswordTitle": "修改密碼", + "@changePasswordTitle": { + "description": "Change password title" + }, + "resetPasswordTitle": "重設密碼", + "@resetPasswordTitle": { + "description": "Reset password title" + }, + "encryptionKeys": "加密金鑰", + "@encryptionKeys": { + "description": "Encryption keys title" + }, + "enterPasswordToEncrypt": "輸入我們可以用來加密您的資料的密碼", + "@enterPasswordToEncrypt": { + "description": "Prompt to enter password for encryption" + }, + "enterNewPasswordToEncrypt": "輸入我們可以用來加密您的資料的新密碼", + "@enterNewPasswordToEncrypt": { + "description": "Prompt to enter new password for encryption" + }, + "passwordWarning": "我們不會儲存這個密碼,所以如果忘記, 我們無法解密您的資料", + "@passwordWarning": { + "description": "Warning about password storage" + }, + "howItWorks": "工作原理", + "@howItWorks": { + "description": "How it works button label" + }, + "generatingEncryptionKeys": "正在產生加密金鑰...", + "@generatingEncryptionKeys": { + "description": "Generating encryption keys message" + }, + "passwordChangedSuccessfully": "密碼修改成功", + "@passwordChangedSuccessfully": { + "description": "Password changed successfully message" + }, + "signOutFromOtherDevices": "從其他裝置登出", + "@signOutFromOtherDevices": { + "description": "Sign out from other devices title" + }, + "signOutOtherBody": "如果您認為有人可能知道您的密碼,您可以強制所有其他使用您帳戶的裝置登出。", + "@signOutOtherBody": { + "description": "Sign out other devices explanation" + }, + "signOutOtherDevices": "登出其他裝置", + "@signOutOtherDevices": { + "description": "Sign out other devices button label" + }, + "doNotSignOut": "不要登出", + "@doNotSignOut": { + "description": "Do not sign out button label" + }, + "generatingEncryptionKeysTitle": "正在產生加密金鑰...", + "@generatingEncryptionKeysTitle": { + "description": "Generating encryption keys title" + }, + "continueLabel": "繼續", + "@continueLabel": { + "description": "Continue button label" + }, + "insecureDevice": "裝置不安全", + "@insecureDevice": { + "description": "Insecure device warning title" + }, + "sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "抱歉,我們無法在此裝置上產生安全金鑰。\n\n請使用其他裝置註冊。", + "@sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": { + "description": "Error message for insecure device" + }, + "recoveryKeyCopiedToClipboard": "復原密鑰已複製到剪貼簿", + "@recoveryKeyCopiedToClipboard": { + "description": "Recovery key copied message" + }, + "recoveryKey": "復原密鑰", + "@recoveryKey": { + "description": "Recovery key label" + }, + "recoveryKeyOnForgotPassword": "如果您忘記了密碼,恢復資料的唯一方法就是使用此金鑰。", + "@recoveryKeyOnForgotPassword": { + "description": "Recovery key importance explanation" + }, + "recoveryKeySaveDescription": "我們不會儲存此金鑰,請將此24個單詞金鑰儲存在一個安全的地方。", + "@recoveryKeySaveDescription": { + "description": "Recovery key save description" + }, + "doThisLater": "稍後再做", + "@doThisLater": { + "description": "Do this later button label" + }, + "saveKey": "儲存金鑰", + "@saveKey": { + "description": "Save key button label" + }, + "recoveryKeySaved": "復原密鑰已儲存在下載資料夾中!", + "@recoveryKeySaved": { + "description": "Recovery key saved confirmation message" + }, + "noRecoveryKeyTitle": "沒有復原密鑰嗎?", + "@noRecoveryKeyTitle": { + "description": "No recovery key title" + }, + "twoFactorAuthTitle": "二步驟驗證", + "@twoFactorAuthTitle": { + "description": "Two-factor authentication title" + }, + "enterCodeHint": "從你的身份驗證器APP中\n輸入6位數字程式碼", + "@enterCodeHint": { + "description": "Hint for entering 2FA code" + }, + "lostDeviceTitle": "遺失了裝置嗎?", + "@lostDeviceTitle": { + "description": "Lost device title" + }, + "enterRecoveryKeyHint": "輸入您的復原密鑰", + "@enterRecoveryKeyHint": { + "description": "Hint for entering recovery key" + }, + "recover": "恢復", + "@recover": { + "description": "Recover button label" + }, + "loggingOut": "正在登出...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "立即", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "APP鎖", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "自動鎖定", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "未找到系統鎖", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "要啟用裝置鎖,請在系統設定中設定裝置密碼或螢幕鎖。", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "在裝置的預設鎖定螢幕和帶有 PIN 或密碼的自訂鎖定螢幕之間進行選擇。", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "裝置鎖", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin 鎖定", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "APP進入後台後鎖定的時間", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "隱藏內容", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "在APP切換器中隱藏APP內容並停用螢幕截圖", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "在APP切換器中隱藏APP內容", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "錯誤的嘗試次數過多", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "點選解鎖", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "您確定要登出嗎?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "是的,登出", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "請進行身份驗證以查看您的金鑰", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "下一步", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "設定新密碼", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "輸入 PIN 碼", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "設定新 PIN 碼", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "確認", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "再次輸入密碼", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "再次輸入 PIN 碼", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "驗證身份", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "未能辨識,請重試。", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "成功", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "取消", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "需要進行身份驗證", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "需要進行生物辨識認證", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "需要裝置憑據", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "需要裝置憑據", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "前往設定", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "您的裝置上未設定生物辨識身份驗證。轉到“設定 > 安全”以加入生物辨識身份驗證。", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "生物辨識身份驗證已停用。請鎖定並解鎖螢幕以啟用該功能。", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "好", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "電子郵件地址已被註冊。", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "電子郵件地址未註冊。", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "該電子郵件已被使用", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "電子郵件已更改為 {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "認證失敗,請重試", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "認證成功!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "工作階段已過期", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "復原密鑰不正確", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "您輸入的復原密鑰不正確", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "二步驟驗證已成功重設", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "yourVerificationCodeHasExpired": "您的驗證碼已過期", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "驗證碼錯誤", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您輸入的驗證碼不正確", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + } +} \ No newline at end of file From c67d2f08363a9ac18469dc0b75b8c6b9932d1bf0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 17:15:17 +0530 Subject: [PATCH 069/164] Consistency --- .../lib/pages/delete_account_page.dart | 8 +++--- .../lib/pages/password_reentry_page.dart | 15 ++++++++--- .../accounts/lib/pages/recovery_page.dart | 4 +-- .../accounts/lib/services/user_service.dart | 25 +++++++++++++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index 66963914d7..f79e32cac8 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -13,10 +13,10 @@ import 'package:ente_utils/platform_util.dart'; import 'package:flutter/material.dart'; class DeleteAccountPage extends StatelessWidget { - final BaseConfiguration configuration; + final BaseConfiguration config; const DeleteAccountPage( - this.configuration, { + this.config, { super.key, }); @@ -169,9 +169,9 @@ class DeleteAccountPage extends StatelessWidget { final decryptChallenge = CryptoUtil.openSealSync( CryptoUtil.base642bin(response.encryptedChallenge), CryptoUtil.base642bin( - configuration.getKeyAttributes()!.publicKey, + config.getKeyAttributes()!.publicKey, ), - configuration.getSecretKey()!, + config.getSecretKey()!, ); final challengeResponseStr = utf8.decode(decryptChallenge); await UserService.instance.deleteAccount(context, challengeResponseStr); diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 7e081c9a5c..f684fc1379 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -15,10 +15,14 @@ import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; class PasswordReentryPage extends StatefulWidget { - final BaseHomePage homePage; final BaseConfiguration config; + final BaseHomePage homePage; - const PasswordReentryPage(this.homePage, this.config, {super.key}); + const PasswordReentryPage( + this.config, + this.homePage, { + super.key, + }); @override State createState() => _PasswordReentryPageState(); @@ -122,7 +126,10 @@ class _PasswordReentryPageState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return RecoveryPage(widget.homePage, widget.config); + return RecoveryPage( + widget.config, + widget.homePage, + ); }, ), ); @@ -272,8 +279,8 @@ class _PasswordReentryPageState extends State { MaterialPageRoute( builder: (BuildContext context) { return RecoveryPage( - widget.homePage, widget.config, + widget.homePage, ); }, ), diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index cc889bc34c..93d22991bf 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -8,10 +8,10 @@ import 'package:ente_ui/utils/toast_util.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { - final BaseHomePage homePage; final BaseConfiguration config; + final BaseHomePage homePage; - const RecoveryPage(this.homePage, this.config, {super.key}); + const RecoveryPage(this.config, this.homePage, {super.key}); @override State createState() => _RecoveryPageState(); diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index f127fe50be..7bc0a54c64 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -348,7 +348,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, @@ -426,9 +429,15 @@ class UserService { await _saveConfiguration(response); if (_config.getEncryptedToken() != null) { if (isResettingPasswordScreen) { - page = RecoveryPage(_homePage, _config); + page = RecoveryPage( + _config, + _homePage, + ); } else { - page = PasswordReentryPage(_homePage, _config); + page = PasswordReentryPage( + _config, + _homePage, + ); } } else { page = PasswordEntryPage( @@ -837,7 +846,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, @@ -1001,7 +1013,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, From e0b3e6464ef2363dc488b4087b9436c2d85d0bfc Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 22 Jul 2025 00:14:27 +0530 Subject: [PATCH 070/164] Update strings --- .../strings/lib/l10n/arb/strings_ar.arb | 20 + .../strings/lib/l10n/arb/strings_bg.arb | 20 + .../strings/lib/l10n/arb/strings_ca.arb | 20 + .../strings/lib/l10n/arb/strings_cs.arb | 20 + .../strings/lib/l10n/arb/strings_da.arb | 20 + .../strings/lib/l10n/arb/strings_de.arb | 20 + .../strings/lib/l10n/arb/strings_el.arb | 20 + .../strings/lib/l10n/arb/strings_en.arb | 20 + .../strings/lib/l10n/arb/strings_es.arb | 22 +- .../strings/lib/l10n/arb/strings_fr.arb | 20 + .../strings/lib/l10n/arb/strings_hu.arb | 20 + .../strings/lib/l10n/arb/strings_id.arb | 20 + .../strings/lib/l10n/arb/strings_it.arb | 20 + .../strings/lib/l10n/arb/strings_ja.arb | 20 + .../strings/lib/l10n/arb/strings_ko.arb | 20 + .../strings/lib/l10n/arb/strings_lt.arb | 20 + .../strings/lib/l10n/arb/strings_nl.arb | 20 + .../strings/lib/l10n/arb/strings_pl.arb | 20 + .../strings/lib/l10n/arb/strings_pt.arb | 20 + .../strings/lib/l10n/arb/strings_ru.arb | 20 + .../strings/lib/l10n/arb/strings_sk.arb | 20 + .../strings/lib/l10n/arb/strings_sl.arb | 20 + .../strings/lib/l10n/arb/strings_sr.arb | 20 + .../strings/lib/l10n/arb/strings_sv.arb | 20 + .../strings/lib/l10n/arb/strings_tr.arb | 20 + .../strings/lib/l10n/arb/strings_uk.arb | 20 + .../strings/lib/l10n/arb/strings_vi.arb | 20 + .../strings/lib/l10n/arb/strings_zh.arb | 20 + .../strings/lib/l10n/arb/strings_zh_CN.arb | 20 + .../strings/lib/l10n/arb/strings_zh_TW.arb | 20 + .../lib/l10n/strings_localizations.dart | 152 +- .../lib/l10n/strings_localizations_ar.dart | 367 ++--- .../lib/l10n/strings_localizations_be.dart | 626 ++++++++ .../lib/l10n/strings_localizations_bg.dart | 367 ++--- .../lib/l10n/strings_localizations_ca.dart | 635 ++++++++ .../lib/l10n/strings_localizations_cs.dart | 361 ++--- .../lib/l10n/strings_localizations_da.dart | 349 +++-- .../lib/l10n/strings_localizations_de.dart | 636 ++++++++ .../lib/l10n/strings_localizations_el.dart | 365 ++--- .../lib/l10n/strings_localizations_en.dart | 28 +- .../lib/l10n/strings_localizations_es.dart | 359 ++--- .../lib/l10n/strings_localizations_et.dart | 629 ++++++++ .../lib/l10n/strings_localizations_fa.dart | 626 ++++++++ .../lib/l10n/strings_localizations_fi.dart | 627 ++++++++ .../lib/l10n/strings_localizations_fr.dart | 362 ++--- .../lib/l10n/strings_localizations_gu.dart | 627 ++++++++ .../lib/l10n/strings_localizations_he.dart | 621 ++++++++ .../lib/l10n/strings_localizations_hi.dart | 627 ++++++++ .../lib/l10n/strings_localizations_hu.dart | 630 ++++++++ .../lib/l10n/strings_localizations_id.dart | 366 ++--- .../lib/l10n/strings_localizations_it.dart | 634 ++++++++ .../lib/l10n/strings_localizations_ja.dart | 375 +++-- .../lib/l10n/strings_localizations_ka.dart | 629 ++++++++ .../lib/l10n/strings_localizations_km.dart | 626 ++++++++ .../lib/l10n/strings_localizations_ko.dart | 380 +++-- .../lib/l10n/strings_localizations_lt.dart | 366 ++--- .../lib/l10n/strings_localizations_lv.dart | 626 ++++++++ .../lib/l10n/strings_localizations_ml.dart | 627 ++++++++ .../lib/l10n/strings_localizations_nl.dart | 370 ++--- .../lib/l10n/strings_localizations_pl.dart | 362 ++--- .../lib/l10n/strings_localizations_pt.dart | 361 ++--- .../lib/l10n/strings_localizations_ro.dart | 631 ++++++++ .../lib/l10n/strings_localizations_ru.dart | 372 ++--- .../lib/l10n/strings_localizations_sk.dart | 663 ++------ .../lib/l10n/strings_localizations_sl.dart | 629 ++++++++ .../lib/l10n/strings_localizations_sr.dart | 368 ++--- .../lib/l10n/strings_localizations_sv.dart | 360 ++--- .../lib/l10n/strings_localizations_ti.dart | 626 ++++++++ .../lib/l10n/strings_localizations_tr.dart | 368 ++--- .../lib/l10n/strings_localizations_uk.dart | 634 ++++++++ .../lib/l10n/strings_localizations_vi.dart | 366 ++--- .../lib/l10n/strings_localizations_zh.dart | 1383 +++++++++++++---- 72 files changed, 17446 insertions(+), 4272 deletions(-) create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_be.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ca.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_de.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_et.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_fa.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_fi.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_gu.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_he.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_hi.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_hu.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_it.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ka.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_km.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_lv.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ml.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ro.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_sl.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_ti.dart create mode 100644 mobile/packages/strings/lib/l10n/strings_localizations_uk.dart diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ar.arb b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb index 89b0360355..916cf68982 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ar.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ar.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "عذراً، الرمز الذي أدخلته غير صحيح", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "اعدادات المطور", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "نقطة طرف الخادم", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "نقطة طرف غير صالحة", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "عذرا، نقطة الطرف التي أدخلتها غير صالحة. فضلا أدخل نقطة طرف صالحة وأعد المحاولة.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "حُدِّثَت نقطة الطرف بنجاح", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_bg.arb b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb index 78ccb634e6..725cc4f991 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_bg.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_bg.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "За съжаление кодът, който сте въвели, е неправилен", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Настройки за програмисти", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Крайна точка на сървъра", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Невалидна крайна точка", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "За съжаление въведената от Вас крайна точка е невалидна. Моля, въведете валидна крайна точка и опитайте отново.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Крайната точка е актуализирана успешно", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ca.arb b/mobile/packages/strings/lib/l10n/arb/strings_ca.arb index b8c332d65f..d277c5ae2c 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ca.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ca.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Ho sentim, el codi que has introduït és incorrecte", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Configuració de desenvolupador", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint del servidor", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Endpoint no vàlid", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Ho sentim, l'endpoint que has introduït no és vàlid. Introdueix un endpoint vàlid i torna-ho a intentar.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Extrem actualitzat correctament", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_cs.arb b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb index ab5b183c46..398127b655 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_cs.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_cs.arb @@ -692,5 +692,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Omlouváme se, zadaný kód je nesprávný", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Nastavení pro vývojáře", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Koncový bod serveru", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Neplatný koncový bod", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Zadaný koncový bod je neplatný. Zadejte prosím platný koncový bod a zkuste to znovu.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Koncový bod byl úspěšně aktualizován", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_da.arb b/mobile/packages/strings/lib/l10n/arb/strings_da.arb index eda7fa3174..23eff3fdf5 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_da.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_da.arb @@ -684,5 +684,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Beklager, den indtastede kode er forkert", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Udvikler-indstillinger", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Server endpoint", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Ugyldigt endpoint", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Beklager, det indtastede endpoint er ugyldigt. Indtast venligst et gyldigt endpoint og forsøg igen.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint opdateret", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_de.arb b/mobile/packages/strings/lib/l10n/arb/strings_de.arb index fd5219b843..7b4a3d0344 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_de.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_de.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Leider ist der eingegebene Code falsch", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Entwicklereinstellungen", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Server Endpunkt", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Ungültiger Endpunkt", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Der eingegebene Endpunkt ist ungültig. Bitte geben Sie einen gültigen Endpunkt ein und versuchen Sie es erneut.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpunkt erfolgreich aktualisiert", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_el.arb b/mobile/packages/strings/lib/l10n/arb/strings_el.arb index cd1bd4f6ed..bd290f6998 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_el.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_el.arb @@ -688,5 +688,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Λυπούμαστε, ο κωδικός που εισαγάγατε είναι εσφαλμένος", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Ρυθμίσεις προγραμματιστή", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Τερματικό σημείο διακομιστή", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Μη έγκυρο τερματικό σημείο", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Λυπούμαστε, το τερματικό σημείο που εισάγατε δεν είναι έγκυρο. Παρακαλώ εισάγετε ένα έγκυρο τερματικό σημείο και προσπαθήστε ξανά.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Το τερματκό σημείο ενημερώθηκε επιτυχώς", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 3a574c2fb0..6db1039109 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -759,5 +759,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, the code you've entered is incorrect", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Developer settings", + "@developerSettings": { + "description": "Label for developer settings" + }, + "serverEndpoint": "Server endpoint", + "@serverEndpoint": { + "description": "Label for server endpoint setting" + }, + "invalidEndpoint": "Invalid endpoint", + "@invalidEndpoint": { + "description": "Error message when endpoint is invalid" + }, + "invalidEndpointMessage": "Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.", + "@invalidEndpointMessage": { + "description": "Detailed error message when endpoint is invalid" + }, + "endpointUpdatedMessage": "Endpoint updated successfully", + "@endpointUpdatedMessage": { + "description": "Success message when endpoint is updated" } } diff --git a/mobile/packages/strings/lib/l10n/arb/strings_es.arb b/mobile/packages/strings/lib/l10n/arb/strings_es.arb index 33fc9fbb9a..dca0601187 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_es.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_es.arb @@ -694,7 +694,27 @@ "description": "Error message when code is incorrect" }, "sorryTheCodeYouveEnteredIsIncorrect": "Lo sentimos, el código que has introducido es incorrecto", - "@sorryTheCodeYouveEnteredIsIncorrect": { + "@sorryTheCodeYouveEnteredIsIncorrected": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Ajustes de desarrollador", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint del servidor", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Endpoint no válido", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Lo sentimos, el endpoint introducido no es válido. Por favor, introduce un endpoint válido y vuelve a intentarlo.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint actualizado con éxito", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_fr.arb b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb index ede2965d7d..7db985902d 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_fr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_fr.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Le code que vous avez saisi est incorrect", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Paramètres du développeur", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Point de terminaison serveur", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Point de terminaison non valide", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Désolé, le point de terminaison que vous avez entré n'est pas valide. Veuillez en entrer un valide puis réessayez.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Point de terminaison mis à jour avec succès", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_hu.arb b/mobile/packages/strings/lib/l10n/arb/strings_hu.arb index cb3fc544f4..0bdbdaaeed 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_hu.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_hu.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Sajnáljuk, a megadott kód helytelen", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Fejlesztői beállítások", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Szerver végpont", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Érvénytelen végpont", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Sajnáljuk, a megadott végpont érvénytelen. Adjon meg egy érvényes végpontot, és próbálja újra.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "A végpont sikeresen frissítve", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_id.arb b/mobile/packages/strings/lib/l10n/arb/strings_id.arb index 6a7c621b5a..22c203c0f5 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_id.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_id.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Maaf, kode yang Anda masukkan takbenar", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Pengaturan Pengembang", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Peladen endpoint", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Endpoint takvalid", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Maaf, endpoint yang Anda masukkan takvalid. Mohon masukkan endpoint yang valid, lalu coba kembali.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint berhasil diubah", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_it.arb b/mobile/packages/strings/lib/l10n/arb/strings_it.arb index 55a82de794..ec8fd65de2 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_it.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_it.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Spiacenti, il codice che hai inserito non è corretto", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Impostazioni sviluppatore", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint del server", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Endpoint invalido", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Spiacenti, l'endpoint inserito non è valido. Inserisci un endpoint valido e riprova.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint aggiornato con successo", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ja.arb b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb index 538533fa1d..cb42550abb 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ja.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ja.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "申し訳ありませんが、入力されたコードは正しくありません", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "開発者向け設定", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "サーバーエンドポイント", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "無効なエンドポイントです", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "エンドポイントの更新に成功しました", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ko.arb b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb index afc2095077..f581bd6667 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ko.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ko.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "죄송합니다, 입력하신 코드가 맞지 않습니다", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "개발자 설정", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "서버 엔드포인트", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "유효하지 않은 엔드포인트", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "죄송합니다, 입력하신 엔드포인트가 유효하지 않습니다. 유효한 엔드포인트를 입력하시고 다시 시도해주세요.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "엔드포인트가 성공적으로 업데이트됨", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_lt.arb b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb index 97121f3a3e..474622e1da 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_lt.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_lt.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Atsiprašome, įvestas kodas yra neteisingas.", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Kūrėjo nustatymai", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Serverio galutinis taškas", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Netinkamas galutinis taškas", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Atsiprašome, įvestas galutinis taškas netinkamas. Įveskite tinkamą galutinį tašką ir bandykite dar kartą.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Galutinis taškas sėkmingai atnaujintas", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_nl.arb b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb index 3ac69c6ce5..a4b1d6ba36 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_nl.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_nl.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, de ingevoerde code is onjuist", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Ontwikkelaarsinstellingen", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Server eindpunt", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Ongeldig eindpunt", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Sorry, het eindpunt dat u hebt ingevoerd is ongeldig. Voer een geldig eindpunt in en probeer het opnieuw.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Eindpunt met succes bijgewerkt", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pl.arb b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb index 8f7c52e031..1cd3b15fdf 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_pl.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_pl.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Niestety, wprowadzony kod jest nieprawidłowy", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Ustawienia dla programistów", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Punkt końcowy serwera", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Punkt końcowy jest nieprawidłowy", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Niestety, wprowadzony punkt końcowy jest nieprawidłowy. Wprowadź prawidłowy punkt końcowy i spróbuj ponownie.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Punkt końcowy zaktualizowany pomyślnie", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_pt.arb b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb index 1fcd31bd01..72e947dc72 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_pt.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_pt.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "O código inserido está incorreto", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Opções de Desenvolvedor", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint do servidor", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Endpoint inválido", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Desculpe, o ponto de acesso inserido é inválido. Insira um ponto de acesso válido e tente novamente.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "O endpoint foi atualizado", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_ru.arb b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb index 3ae06044a0..80b84d8de8 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_ru.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_ru.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Извините, введенный вами код неверный", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Настройки разработчика", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Конечная точка сервера", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Неверная конечная точка", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Извините, введенная вами конечная точка неверна. Пожалуйста, введите корректную конечную точку и повторите попытку.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Конечная точка успешно обновлена", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sk.arb b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb index 6ab9ffae97..e219a19391 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sk.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sk.arb @@ -688,5 +688,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Ľutujeme, zadaný kód je nesprávny", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Nastavenia pre vývojárov", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint servera", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Neplatný endpoint", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Ospravedlňujeme sa, endpoint, ktorý ste zadali, je neplatný. Zadajte platný endpoint a skúste to znova.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint úspešne aktualizovaný", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sl.arb b/mobile/packages/strings/lib/l10n/arb/strings_sl.arb index 8112048df0..e8d3845abc 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sl.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sl.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Oprostite, koda ki ste jo vnesli ni pravilna", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Nastavitve za razvijalce", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Endpoint strežnika", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Nepravilen endpoint", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Oprostite endpoint, ki ste ga vnesli ni bil pravilen. Prosimo, vnesite pravilen endpoint in poskusite znova.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Endpoint posodobljen uspešno", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sr.arb b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb index 8d8f41e93e..4d9592d94e 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sr.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Унет кôд није добар", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Подешавања за програмере", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Крајња тачка сервера", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Погрешна крајња тачка", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Извини, крајња тачка коју си унео је неважећа. Унеси важећу крајњу тачку и покушај поново.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Крајна тачка успешно ажурирана", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_sv.arb b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb index 6940c3eedb..924ef97553 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_sv.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_sv.arb @@ -692,5 +692,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Tyvärr, den kod som du har angett är felaktig", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Utvecklarinställningar", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Serverns slutpunkt", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Ogiltig slutpunkt", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Tyvärr, slutpunkten du angav är ogiltig. Ange en giltig slutpunkt och försök igen.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Slutpunkten har uppdaterats", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_tr.arb b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb index 2a0ef1e277..39ee00c798 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_tr.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_tr.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Üzgünüz, girdiğiniz kod yanlış", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Geliştirici ayarları", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Sunucu uç noktası", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Geçersiz uç nokta", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Üzgünüz, girdiğiniz uç nokta geçersiz. Lütfen geçerli bir uç nokta girin ve tekrar deneyin.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Uç nokta başarıyla güncellendi", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_uk.arb b/mobile/packages/strings/lib/l10n/arb/strings_uk.arb index 38a754283c..432de3e5dd 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_uk.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_uk.arb @@ -688,5 +688,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Вибачте, але введений вами код є невірним", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Налаштування розробника", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Кінцева точка сервера", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Некоректна кінцева точка", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Вибачте, введена вами кінцева точка є недійсною. Введіть дійсну кінцеву точку та спробуйте ще раз.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Точка входу успішно оновлена", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_vi.arb b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb index 2f98a1ac2d..b571755af6 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_vi.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_vi.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "Xin lỗi, mã bạn đã nhập không chính xác", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Cài đặt cho nhà phát triển", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "Điểm cuối máy chủ", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "Điểm cuối không hợp lệ", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "Xin lỗi, điểm cuối bạn nhập không hợp lệ. Vui lòng nhập một điểm cuối hợp lệ và thử lại.", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "Cập nhật điểm cuối thành công", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb index d55445222b..82f2b20bd1 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_zh.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh.arb @@ -696,5 +696,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您输入的验证码不正确", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "开发者设置", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "服务器端点", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "端点无效", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "端点更新成功", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb index ac625670ad..a7fdb432a6 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您输入的验证码不正确", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "开发者设置", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "服务器端点", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "端点无效", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "抱歉,您输入的端点无效。请输入有效的端点,然后重试。", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "端点更新成功", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb index 54a6df72a8..a5f30aab0e 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb @@ -700,5 +700,25 @@ "sorryTheCodeYouveEnteredIsIncorrect": "抱歉,您輸入的驗證碼不正確", "@sorryTheCodeYouveEnteredIsIncorrect": { "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "開發者設定", + "@developerSettings": { + "description": "Label for developerSettings" + }, + "serverEndpoint": "伺服器端點", + "@serverEndpoint": { + "description": "Label for serverEndpoint" + }, + "invalidEndpoint": "端點無效", + "@invalidEndpoint": { + "description": "Label for invalidEndpoint" + }, + "invalidEndpointMessage": "抱歉,您輸入的端點無效。請輸入有效的端點,然後重試。", + "@invalidEndpointMessage": { + "description": "Label for invalidEndpointMessage" + }, + "endpointUpdatedMessage": "端點更新成功", + "@endpointUpdatedMessage": { + "description": "Label for endpointUpdatedMessage" } } \ No newline at end of file diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index 5daf0f0c71..c0ec261268 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -6,25 +6,44 @@ import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; import 'strings_localizations_ar.dart'; +import 'strings_localizations_be.dart'; import 'strings_localizations_bg.dart'; +import 'strings_localizations_ca.dart'; import 'strings_localizations_cs.dart'; import 'strings_localizations_da.dart'; +import 'strings_localizations_de.dart'; import 'strings_localizations_el.dart'; import 'strings_localizations_en.dart'; import 'strings_localizations_es.dart'; +import 'strings_localizations_et.dart'; +import 'strings_localizations_fa.dart'; +import 'strings_localizations_fi.dart'; import 'strings_localizations_fr.dart'; +import 'strings_localizations_gu.dart'; +import 'strings_localizations_he.dart'; +import 'strings_localizations_hi.dart'; +import 'strings_localizations_hu.dart'; import 'strings_localizations_id.dart'; +import 'strings_localizations_it.dart'; import 'strings_localizations_ja.dart'; +import 'strings_localizations_ka.dart'; +import 'strings_localizations_km.dart'; import 'strings_localizations_ko.dart'; import 'strings_localizations_lt.dart'; +import 'strings_localizations_lv.dart'; +import 'strings_localizations_ml.dart'; import 'strings_localizations_nl.dart'; import 'strings_localizations_pl.dart'; import 'strings_localizations_pt.dart'; +import 'strings_localizations_ro.dart'; import 'strings_localizations_ru.dart'; import 'strings_localizations_sk.dart'; +import 'strings_localizations_sl.dart'; import 'strings_localizations_sr.dart'; import 'strings_localizations_sv.dart'; +import 'strings_localizations_ti.dart'; import 'strings_localizations_tr.dart'; +import 'strings_localizations_uk.dart'; import 'strings_localizations_vi.dart'; import 'strings_localizations_zh.dart'; @@ -116,27 +135,47 @@ abstract class StringsLocalizations { /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('ar'), + Locale('be'), Locale('bg'), + Locale('ca'), Locale('cs'), Locale('da'), + Locale('de'), Locale('el'), Locale('en'), Locale('es'), + Locale('et'), + Locale('fa'), + Locale('fi'), Locale('fr'), + Locale('gu'), + Locale('he'), + Locale('hi'), + Locale('hu'), Locale('id'), + Locale('it'), Locale('ja'), + Locale('ka'), + Locale('km'), Locale('ko'), Locale('lt'), + Locale('lv'), + Locale('ml'), Locale('nl'), Locale('pl'), Locale('pt'), + Locale('ro'), Locale('ru'), Locale('sk'), + Locale('sl'), Locale('sr'), Locale('sv'), + Locale('ti'), Locale('tr'), + Locale('uk'), Locale('vi'), Locale('zh'), + Locale('zh', 'CN'), Locale('zh', 'TW') ]; @@ -260,30 +299,6 @@ abstract class StringsLocalizations { /// **'N/A'** String get notAvailable; - /// Prefix for log file names - /// - /// In en, this message translates to: - /// **'ente-logs-'** - String get enteLogsPrefix; - - /// Name of logs directory - /// - /// In en, this message translates to: - /// **'logs'** - String get logsDirectoryName; - - /// Name of zipped log file - /// - /// In en, this message translates to: - /// **'logs.zip'** - String get logsZipFileName; - - /// File extension for zip files - /// - /// In en, this message translates to: - /// **'zip'** - String get zipFileExtension; - /// Label for reporting a bug /// /// In en, this message translates to: @@ -1249,6 +1264,36 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'Sorry, the code you\'ve entered is incorrect'** String get sorryTheCodeYouveEnteredIsIncorrect; + + /// Label for developer settings + /// + /// In en, this message translates to: + /// **'Developer settings'** + String get developerSettings; + + /// Label for server endpoint setting + /// + /// In en, this message translates to: + /// **'Server endpoint'** + String get serverEndpoint; + + /// Error message when endpoint is invalid + /// + /// In en, this message translates to: + /// **'Invalid endpoint'** + String get invalidEndpoint; + + /// Detailed error message when endpoint is invalid + /// + /// In en, this message translates to: + /// **'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'** + String get invalidEndpointMessage; + + /// Success message when endpoint is updated + /// + /// In en, this message translates to: + /// **'Endpoint updated successfully'** + String get endpointUpdatedMessage; } class _StringsLocalizationsDelegate @@ -1264,25 +1309,44 @@ class _StringsLocalizationsDelegate @override bool isSupported(Locale locale) => [ 'ar', + 'be', 'bg', + 'ca', 'cs', 'da', + 'de', 'el', 'en', 'es', + 'et', + 'fa', + 'fi', 'fr', + 'gu', + 'he', + 'hi', + 'hu', 'id', + 'it', 'ja', + 'ka', + 'km', 'ko', 'lt', + 'lv', + 'ml', 'nl', 'pl', 'pt', + 'ro', 'ru', 'sk', + 'sl', 'sr', 'sv', + 'ti', 'tr', + 'uk', 'vi', 'zh' ].contains(locale.languageCode); @@ -1297,6 +1361,8 @@ StringsLocalizations lookupStringsLocalizations(Locale locale) { case 'zh': { switch (locale.countryCode) { + case 'CN': + return StringsLocalizationsZhCn(); case 'TW': return StringsLocalizationsZhTw(); } @@ -1308,44 +1374,82 @@ StringsLocalizations lookupStringsLocalizations(Locale locale) { switch (locale.languageCode) { case 'ar': return StringsLocalizationsAr(); + case 'be': + return StringsLocalizationsBe(); case 'bg': return StringsLocalizationsBg(); + case 'ca': + return StringsLocalizationsCa(); case 'cs': return StringsLocalizationsCs(); case 'da': return StringsLocalizationsDa(); + case 'de': + return StringsLocalizationsDe(); case 'el': return StringsLocalizationsEl(); case 'en': return StringsLocalizationsEn(); case 'es': return StringsLocalizationsEs(); + case 'et': + return StringsLocalizationsEt(); + case 'fa': + return StringsLocalizationsFa(); + case 'fi': + return StringsLocalizationsFi(); case 'fr': return StringsLocalizationsFr(); + case 'gu': + return StringsLocalizationsGu(); + case 'he': + return StringsLocalizationsHe(); + case 'hi': + return StringsLocalizationsHi(); + case 'hu': + return StringsLocalizationsHu(); case 'id': return StringsLocalizationsId(); + case 'it': + return StringsLocalizationsIt(); case 'ja': return StringsLocalizationsJa(); + case 'ka': + return StringsLocalizationsKa(); + case 'km': + return StringsLocalizationsKm(); case 'ko': return StringsLocalizationsKo(); case 'lt': return StringsLocalizationsLt(); + case 'lv': + return StringsLocalizationsLv(); + case 'ml': + return StringsLocalizationsMl(); case 'nl': return StringsLocalizationsNl(); case 'pl': return StringsLocalizationsPl(); case 'pt': return StringsLocalizationsPt(); + case 'ro': + return StringsLocalizationsRo(); case 'ru': return StringsLocalizationsRu(); case 'sk': return StringsLocalizationsSk(); + case 'sl': + return StringsLocalizationsSl(); case 'sr': return StringsLocalizationsSr(); case 'sv': return StringsLocalizationsSv(); + case 'ti': + return StringsLocalizationsTi(); case 'tr': return StringsLocalizationsTr(); + case 'uk': + return StringsLocalizationsUk(); case 'vi': return StringsLocalizationsVi(); case 'zh': diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 8ebac5e778..cdffa58cc6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -14,40 +14,40 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'غير قادر على الاتصال بـ Ente، يرجى إعادة المحاولة بعد فترة. إذا استمر الخطأ، يرجى الاتصال بالدعم.'; + 'تعذر الإتصال بـEnte، فضلا أعد المحاولة لاحقا. إذا استمر الخطأ، فضلا تواصل مع الدعم.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'يبدو أن خطأ ما حدث. يرجى إعادة المحاولة بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.'; + 'يبدو أنه حدث خطأ ما. الرجاء إعادة المحاولة لاحقا. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم.'; @override String get error => 'خطأ'; @override - String get ok => 'موافق'; + String get ok => 'حسناً'; @override - String get faq => 'الأسئلة الشائعة'; + String get faq => 'الأسئلة الأكثر شيوعاً'; @override - String get contactSupport => 'اتصل بالدعم'; + String get contactSupport => 'الاتصال بالدعم'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'إرسال السجلات عبر البريد الإلكتروني'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'الرجاء إرسال السجلات إلى $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'نسخ عنوان البريد الإلكتروني'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'تصدير السجلات'; @override - String get cancel => 'Cancel'; + String get cancel => 'إلغاء'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsAr extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'ألإبلاغ عن خلل تقني'; @override String get logsDialogBody => @@ -100,505 +88,505 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'متصل بـ$endpoint'; } @override - String get save => 'Save'; + String get save => 'حفظ'; @override - String get send => 'Send'; + String get send => 'إرسال'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا) أم إرساله إلى تطبيقات أخرى؟'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا)؟'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'أدخل عنوان بريدك الإلكتروني الجديد'; @override - String get email => 'Email'; + String get email => 'البريد الإلكتروني'; @override - String get verify => 'Verify'; + String get verify => 'التحقق'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'عنوان البريد الإلكتروني غير صالح'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'الرجاء إدخال بريد إلكتروني صالح.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'انتظر قليلاً...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'التحقق من كلمة المرور'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'كلمة المرور غير صحيحة'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'يرجى المحاولة مرة أخرى'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'أدخل كلمة المرور'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'أدخل كلمة المرور الخاصة بك'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'الجلسات النشطة'; @override - String get oops => 'Oops'; + String get oops => 'عذرًا'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'حدث خطأ ما، يرجى المحاولة مرة أخرى'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'إنهاء الجلسة؟'; @override - String get terminate => 'Terminate'; + String get terminate => 'إنهاء'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'هذا الجهاز'; @override - String get createAccount => 'Create account'; + String get createAccount => 'إنشاء حساب'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'ضعيف'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'متوسط'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'قوي'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'إزالة الحساب'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'سوف نأسف لرؤيتك تذهب. هل تواجه بعض المشاكل؟'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'نعم، ارسل الملاحظات'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'لا، حذف الحساب'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => 'الرجاء المصادقة لبدء حذف الحساب'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'تأكيد حذف الحساب'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'هذا الحساب مرتبط بتطبيقات Ente أخرى، إذا كنت تستخدم أحدها.\n\nسنضع موعدا لحذف بياناتك المرفوعة عبر كل تطبيقات Ente، وسيتم حذف حسابك بصورة دائمة.'; @override - String get delete => 'Delete'; + String get delete => 'حذف'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'إنشاء حساب جديد'; @override - String get password => 'Password'; + String get password => 'كلمة المرور'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'تأكيد كلمة المرور'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'قوة كلمة المرور: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'كيف سمعت عن Ente؟ (اختياري)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'نحن لا نتتبع تثبيت التطبيق. سيكون من المفيد إذا أخبرتنا أين وجدتنا!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'أوافق على شروط الخدمة وسياسة الخصوصية'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'الشروط'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'سياسة الخصوصية'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'أنا أفهم أنه إذا فقدت كلمة المرور الخاصة بي، قد أفقد بياناتي لأن بياناتي هي مشفرة من الند للند.'; @override - String get encryption => 'Encryption'; + String get encryption => 'التشفير'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'تسجيل الدخول'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'مرحبًا مجددًا!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'بالنقر على تسجيل الدخول، أوافق على شروط الخدمة و سياسة الخصوصية'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'لا يوجد اتصال بالإنترنت'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'يرجى التحقق من اتصالك بالإنترنت ثم المحاولة من جديد.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'فشل في المصادقة ، يرجى المحاولة مرة أخرى في وقت لاحق'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'إعادة كتابة كلمة المرور'; @override String get recreatePasswordBody => - '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).'; + 'الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'استخدم مفتاح الاسترداد'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'هل نسيت كلمة المرور'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'غير البريد الإلكتروني'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'تأكيد البريد الإلكتروني'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'لقد أرسلنا رسالة إلى $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'لإعادة تعيين كلمة المرور الخاصة بك، يرجى التحقق من بريدك الإلكتروني أولاً.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'الرجاء التحقق من صندوق الوارد (والرسائل غير المرغوب فيها) لإكمال التحقق'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'انقر لإدخال الرمز'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'إرسال بريد إلكتروني'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'إعادة إرسال البريد الإلكتروني'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'التحقق ما زال جارٍ'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'انتهت صلاحية الجلسة'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'انتهت صلاحية جلستك. فضلا أعد تسجيل الدخول.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'التحقق من مفتاح المرور'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'بانتظار التحقق...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'حاول مرة أخرى'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'تحقق من الحالة'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => ''; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'إسترجاع الحساب'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'تعيين كلمة المرور'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'تغيير كلمة المرور'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'إعادة تعيين كلمة المرور'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'مفاتيح التشفير'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'أدخل كلمة المرور التي يمكننا استخدامها لتشفير بياناتك'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'أدخل كلمة مرور جديدة يمكننا استخدامها لتشفير بياناتك'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، لا يمكننا فك تشفير بياناتك'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'كيف يعمل'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'توليد مفاتيح التشفير...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'تم تغيير كلمة المرور بنجاح'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'إذا كنت تعتقد أن شخصا ما يعرف كلمة المرور الخاصة بك، يمكنك إجبار جميع الأجهزة الأخرى الستخدمة حاليا لحسابك على تسجيل الخروج.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'لا تقم بتسجيل الخروج'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'توليد مفاتيح التشفير...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'المتابعة'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'جهاز غير آمن'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'عذرًا، لم نتمكن من إنشاء مفاتيح آمنة على هذا الجهاز.\n\nيرجى التسجيل من جهاز مختلف.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => 'تم نسخ عبارة الاسترداد للحافظة'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'مفتاح الاسترداد'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد بياناتك هي بهذا المفتاح.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'نحن لا نخزن هذا المفتاح، يرجى حفظ مفتاح الـ 24 كلمة هذا في مكان آمن.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'قم بهذا لاحقاً'; @override - String get saveKey => 'Save key'; + String get saveKey => 'حفظ المفتاح'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => 'حُفِظ مفتاح الاستعادة في مجلد التنزيلات!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'لا يوجد مفتاح استرجاع؟'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'المصادقة الثنائية'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => 'أدخل الرمز المكون من 6 أرقام من\nتطبيق المصادقة'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'جهاز مفقود ؟'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'أدخل رمز الاسترداد'; @override - String get recover => 'Recover'; + String get recover => 'استرداد'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'جاري تسجيل الخروج...'; @override - String get immediately => 'Immediately'; + String get immediately => 'فورًا'; @override - String get appLock => 'App lock'; + String get appLock => 'قُفْل التطبيق'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'قفل تلقائي'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'لا يوجد قفل نظام'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'لتفعيل قُفْل الجهاز، اضبط رمز مرور أو قُفْل الشاشة من الإعدادات'; @override - String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + String get appLockDescription => 'اختر نوع قُفْل الشاشة: افتراضي أو مخصص.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'قفل الجهاز'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'قفل رقم التعريف الشخصي'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'الوقت الذي بعده ينقفل التطبيق بعدما يوضع في الخلفية'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'أخفِ المحتوى'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'يخفي محتوى التطبيق في مبدل التطبيقات ويمنع لقطات الشاشة'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'يخفي محتوى التطبيق في مبدل التطبيقات'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'محاولات خاطئة أكثر من المسموح'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'المس لإلغاء القفل'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'هل أنت متأكد من أنك تريد تسجيل الخروج؟'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'نعم، تسجيل الخروج'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك'; @override - String get next => 'Next'; + String get next => 'التالي'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'عين كلمة مرور جديدة'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'أدخل رقم التعريف الشخصي'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'عين رقم تعريف شخصي جديد'; @override - String get confirm => 'Confirm'; + String get confirm => 'تأكيد'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'أعد إدخال كلمة المرور'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'أعد إدخال رقم التعريف الشخصي'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'التحقق من الهوية'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'لم يتم التعرف عليه. حاول مرة أخرى.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'تم بنجاح'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'إلغاء'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'المصادقة مطلوبة'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'البيومترية مطلوبة'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'بيانات اعتماد الجهاز مطلوبة'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'بيانات اعتماد الجهاز مطلوبة'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'الانتقال إلى الإعدادات'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'لم يتم إعداد المصادقة الحيوية على جهازك. انتقل إلى \'الإعدادات > الأمن\' لإضافة المصادقة البيومترية.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'المصادقة البيومترية معطلة. الرجاء قفل الشاشة وفتح القفل لتفعيلها.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'حسناً'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'البريد الإلكتروني مُسَجَّل من قبل.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'البريد الإلكتروني غير مُسَجَّل.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'هذا البريد مستخدم مسبقاً'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'تم تغيير البريد الإلكتروني إلى $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'فشلت المصادقة. الرجاء المحاولة مرة أخرى'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'تمت المصادقة بنجاح!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'انتهت صَلاحِيَة الجِلسة'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'مفتاح الاسترداد غير صحيح'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'مفتاح الاسترداد الذي أدخلته غير صحيح'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'تم تحديث المصادقة الثنائية بنجاح'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +598,28 @@ class StringsLocalizationsAr extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => 'انتهت صلاحية رمز التحقق'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'رمز غير صحيح'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'عذراً، الرمز الذي أدخلته غير صحيح'; + + @override + String get developerSettings => 'اعدادات المطور'; + + @override + String get serverEndpoint => 'نقطة طرف الخادم'; + + @override + String get invalidEndpoint => 'نقطة طرف غير صالحة'; + + @override + String get invalidEndpointMessage => + 'عذرا، نقطة الطرف التي أدخلتها غير صالحة. فضلا أدخل نقطة طرف صالحة وأعد المحاولة.'; + + @override + String get endpointUpdatedMessage => 'حُدِّثَت نقطة الطرف بنجاح'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_be.dart b/mobile/packages/strings/lib/l10n/strings_localizations_be.dart new file mode 100644 index 0000000000..3ff70f8c41 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_be.dart @@ -0,0 +1,626 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Belarusian (`be`). +class StringsLocalizationsBe extends StringsLocalizations { + StringsLocalizationsBe([String locale = 'be']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Памылка'; + + @override + String get ok => 'OK'; + + @override + String get faq => 'Частыя пытанні'; + + @override + String get contactSupport => 'Звярнуцца ў службу падтрымкі'; + + @override + String get emailYourLogs => 'Адправіць журналы'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Экспартаваць журналы'; + + @override + String get cancel => 'Скасаваць'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Паведаміць аб памылцы'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Захаваць'; + + @override + String get send => 'Адправіць'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Электронная пошта'; + + @override + String get verify => 'Праверыць'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Пачакайце...'; + + @override + String get verifyPassword => 'Праверыць пароль'; + + @override + String get incorrectPasswordTitle => 'Няправільны пароль'; + + @override + String get pleaseTryAgain => 'Калі ласка, паспрабуйце яшчэ раз'; + + @override + String get enterPassword => 'Увядзіце пароль'; + + @override + String get enterYourPasswordHint => 'Увядзіце ваш пароль'; + + @override + String get activeSessions => 'Актыўныя сеансы'; + + @override + String get oops => 'Вой'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Перарваць'; + + @override + String get thisDevice => 'Гэта прылада'; + + @override + String get createAccount => 'Стварыць уліковы запіс'; + + @override + String get weakStrength => 'Ненадзейны'; + + @override + String get moderateStrength => 'Умераная'; + + @override + String get strongStrength => 'Надзейны'; + + @override + String get deleteAccount => 'Выдаліць уліковы запіс'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'Не, выдаліць уліковы запіс'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Выдаліць'; + + @override + String get createNewAccount => 'Стварыць новы ўліковы запіс'; + + @override + String get password => 'Пароль'; + + @override + String get confirmPassword => 'Пацвердзіць пароль'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Умовы'; + + @override + String get privacyPolicyTitle => 'Палітыка прыватнасці'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Шыфраванне'; + + @override + String get logInLabel => 'Увайсці'; + + @override + String get welcomeBack => 'З вяртаннем!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Выкарыстоўваць ключ аднаўлення'; + + @override + String get forgotPassword => 'Забылі пароль'; + + @override + String get changeEmail => 'Змяніць адрас электроннай пошты'; + + @override + String get verifyEmail => 'Праверыць электронную пошту'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Адправіць ліст'; + + @override + String get resendEmail => 'Адправіць ліст яшчэ раз'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Сеанс завяршыўся'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Паспрабуйце яшчэ раз'; + + @override + String get checkStatus => 'Праверыць статус'; + + @override + String get loginWithTOTP => 'Увайсці з TOTP'; + + @override + String get recoverAccount => 'Аднавіць уліковы запіс'; + + @override + String get setPasswordTitle => 'Задаць пароль'; + + @override + String get changePasswordTitle => 'Змяніць пароль'; + + @override + String get resetPasswordTitle => 'Скінуць пароль'; + + @override + String get encryptionKeys => 'Ключы шыфравання'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'Як гэта працуе'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Не выходзіць'; + + @override + String get generatingEncryptionKeysTitle => 'Генерацыя ключоў шыфравання...'; + + @override + String get continueLabel => 'Працягнуць'; + + @override + String get insecureDevice => 'Небяспечная прылада'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Ключ аднаўлення'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Зрабіць гэта пазней'; + + @override + String get saveKey => 'Захаваць ключ'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Згубілі прыладу?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Аднавіць'; + + @override + String get loggingOut => 'Выхад...'; + + @override + String get immediately => 'Адразу'; + + @override + String get appLock => 'Блакіроўка праграмы'; + + @override + String get autoLock => 'Аўтаблакіроўка'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Блакіроўка прылады'; + + @override + String get pinLock => 'Блакіроўка PIN\'ам'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Схаваць змест'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Націсніце для разблакіроўкі'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Так, выйсці'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Далей'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Увядзіце PIN-код'; + + @override + String get setNewPin => 'Задаць новы PIN'; + + @override + String get confirm => 'Пацвердзіць'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Увядзіце PIN-код яшчэ раз'; + + @override + String get androidBiometricHint => 'Праверыць ідэнтыфікацыю'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Паспяхова'; + + @override + String get androidCancelButton => 'Скасаваць'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Перайсці ў налады'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Сеанс завяршыўся'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Няправільны код'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 14cf505ae9..a79fe0c723 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -14,40 +14,40 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Не може да се свърже с Ente, моля, опитайте отново след известно време. Ако проблемът продължава, моля, свържете се с поддръжката.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Изглежда нещо се обърка. Моля, опитайте отново след известно време. Ако грешката продължава, моля, свържете се с нашия екип за поддръжка.'; @override - String get error => 'Error'; + String get error => 'Грешка'; @override - String get ok => 'Ok'; + String get ok => 'Ок'; @override - String get faq => 'FAQ'; + String get faq => 'ЧЗВ'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Свържете се с поддръжката'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Изпратете Вашата история на действията на имейл'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Моля, изпратете историята на действията на \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Копиране на имейл адрес'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Експорт на файловете с историята'; @override - String get cancel => 'Cancel'; + String get cancel => 'Отказ'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsBg extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Докладване на проблем'; @override String get logsDialogBody => @@ -100,505 +88,512 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Свързан към $endpoint'; } @override - String get save => 'Save'; + String get save => 'Запазване'; @override - String get send => 'Send'; + String get send => 'Изпращане'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране) или да го изпратите на други приложения?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Имейл'; @override - String get verify => 'Verify'; + String get verify => 'Потвърждаване'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Невалиден имейл адрес'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Моля, въведете валиден имейл адрес.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Моля изчакайте...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Потвърдете паролата'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Грешна парола'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Опитайте отново'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Въведете парола'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Въведете паролата си'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активни сесии'; @override - String get oops => 'Oops'; + String get oops => 'Опа'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Нещо се обърка, моля опитайте отново'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Това ще Ви изкара от профила на това устройство!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Това ще Ви изкара от профила на следното устройство:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Прекратяване на сесията?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Прекратяване'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Това устройство'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Създаване на акаунт'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слаба'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Умерена'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Силна'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Изтриване на акаунта'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Ще съжаляваме да си тръгнете. Изправени ли сте пред някакъв проблем?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, изпращане на обратна връзка'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Не, изтриване на акаунта'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Моля, удостоверете се, за да инициирате изтриването на акаунта'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Потвърдете изтриването на акаунта'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Този акаунт е свързан с други приложения на Ente, ако използвате такива.\n\nВашите качени данни във всички приложения на Ente ще бъдат планирани за изтриване и акаунтът Ви ще бъде изтрит за постоянно.'; @override - String get delete => 'Delete'; + String get delete => 'Изтриване'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Създаване на нов акаунт'; @override - String get password => 'Password'; + String get password => 'Парола'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Потвърждаване на паролата'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Сила на паролата: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Как научихте за Ente? (по избор)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Ние не проследяваме инсталиранията на приложения. Ще помогне, ако ни кажете къде ни намерихте!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Съгласявам се с условията за ползване и политиката за поверителност'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Условия'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика за поверителност'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Разбирам, че ако загубя паролата си, може да загубя данните си, тъй като данните ми са шифровани от край до край.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифроване'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Вход'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Добре дошли отново!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'С натискането на вход, се съгласявам с условията за ползване и политиката за поверителност'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Няма връзка с интернет'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Моля, проверете интернет връзката си и опитайте отново.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Неуспешно проверка, моля опитайте отново'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Създайте отново парола'; @override String get recreatePasswordBody => - '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).'; + 'Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, но можем да я регенерираме по начин, който работи с всички устройства.\n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Използвайте ключ за възстановяване'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Забравена парола'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Промяна на имейл'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Потвърдете имейла'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Изпратихме имейл до $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'За да нулирате паролата си, моля, първо потвърдете своя имейл.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Моля, проверете входящата си поща (и спама), за да завършите проверката'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Докоснете, за да въведете код'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Изпратете имейл'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Повторно изпращане на имейл'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Потвърждението все още се изчаква'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сесията изтече'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Вашата сесия изтече. Моля влезте отново.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Удостоверяване с ключ за парола'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Изчаква се потвърждение...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Опитайте отново'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Проверка на състоянието'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Влизане с еднократен код'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Възстановяване на акаунт'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Задаване на парола'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Промяна на паролата'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Нулиране на паролата'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Ключове за шифроване'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Въведете парола, която да използваме за шифроване на Вашите данни'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Въведете нова парола, която да използваме за шифроване на Вашите данни'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Ние не съхраняваме тази парола, така че ако я забравите, не можем да дешифрираме Вашите данни'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Как работи'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Генериране на ключове за шифроване...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Паролата е променена успешно'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Излизане от други устройства'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ако смятате, че някой може да знае паролата Ви, можете да принудите всички други устройства, използващи Вашия акаунт, да излязат.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Излизане от други устройства'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не излизайте'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Генерират се ключове за шифроване...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Продължете'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Несигурно устройство'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'За съжаление не можахме да генерираме защитени ключове на това устройство.\n\nМоля, регистрирайте се от друго устройство.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Ключът за възстановяване е копиран в буферната памет'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Ключ за възстановяване'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ако забравите паролата си, единственият начин да възстановите данните си е с този ключ.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Ние не съхраняваме този ключ, моля, запазете този ключ от 24 думи на сигурно място.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Направете това по-късно'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Запазване на ключа'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Ключът за възстановяване е запазен в папка за Изтегляния!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Няма ключ за възстановяване?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Двуфакторно удостоверяване'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Въведете 6-цифрения код от\nВашето приложение за удостоверяване'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Загубено устройство?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Въведете Вашия ключ за възстановяване'; @override - String get recover => 'Recover'; + String get recover => 'Възстановяване'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Излизане от профила...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Незабавно'; @override - String get appLock => 'App lock'; + String get appLock => 'Заключване на приложението'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Автоматично заключване'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Не е намерено заключване на системата'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'За да активирате заключването на устройството, моля, задайте парола за устройството или заключване на екрана в системните настройки.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Изберете между заключен екран по подразбиране на Вашето устройство и персонализиран заключен екран с ПИН код или парола.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Заключване на устройството'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Заключване с ПИН код'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Време, след което приложението се заключва, след като е поставено на заден план'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Скриване на съдържанието'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Скрива съдържанието на приложението в превключвателя на приложения и деактивира екранните снимки'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Скрива съдържанието на приложението в превключвателя на приложения'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Твърде много неуспешни опити'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Докоснете, за да отключите'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Наистина ли искате да излезете от профила си?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Да, излез'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Моля, удостоверете се, за да видите Вашите кодове'; @override - String get next => 'Next'; + String get next => 'Следващ'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Задаване на нова парола'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Въведете ПИН код'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Задаване на нов ПИН код'; @override - String get confirm => 'Confirm'; + String get confirm => 'Потвърждаване'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Въведете отново паролата'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Въведете отново ПИН кода'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Потвърждаване на самоличността'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Не е разпознат. Опитайте отново.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Успешно'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Отказ'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Необходимо е удостоверяване'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Изискват се биометрични данни'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Изискват се идентификационни данни за устройството'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Изискват се идентификационни данни за устройството'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Отваряне на настройките'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Биометричното удостоверяване не е настроено на Вашето устройство. Отидете на „Настройки > Сигурност“, за да добавите биометрично удостоверяване.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Биометричното удостоверяване е деактивирано. Моля, заключете и отключете екрана си, за да го активирате.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'ОК'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Имейлът вече е регистриран.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Имейлът не е регистриран.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Този имейл вече се използва'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Имейлът е променен на $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Неуспешно удостоверяване, моля опитайте отново'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Успешно удостоверяване!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Сесията е изтекла'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Неправилен ключ за възстановяване'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Въведеният от Вас ключ за възстановяване е неправилен'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Двуфакторното удостоверяване бе успешно нулирано'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +606,28 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Вашият код за потвърждение е изтекъл'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Неправилен код'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'За съжаление кодът, който сте въвели, е неправилен'; + + @override + String get developerSettings => 'Настройки за програмисти'; + + @override + String get serverEndpoint => 'Крайна точка на сървъра'; + + @override + String get invalidEndpoint => 'Невалидна крайна точка'; + + @override + String get invalidEndpointMessage => + 'За съжаление въведената от Вас крайна точка е невалидна. Моля, въведете валидна крайна точка и опитайте отново.'; + + @override + String get endpointUpdatedMessage => 'Крайната точка е актуализирана успешно'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ca.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ca.dart new file mode 100644 index 0000000000..e4078b94c0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ca.dart @@ -0,0 +1,635 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Catalan Valencian (`ca`). +class StringsLocalizationsCa extends StringsLocalizations { + StringsLocalizationsCa([String locale = 'ca']) : super(locale); + + @override + String get networkHostLookUpErr => + 'No s\'ha pogut connectar a Ente, si us plau, comprova la configuració de la xarxa i contacta amb suport si l\'error persisteix.'; + + @override + String get networkConnectionRefusedErr => + 'No s\'ha pogut connectar a Ente, si us plau, torna-ho a intentar després d\'un temps. Si l\'error persisteix, contacta amb suport.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Sembla que alguna cosa ha anat malament. Si us plau, torna-ho a intentar després d\'un temps. Si l\'error persisteix, contacta amb el nostre equip de suport.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'D\'acord'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contacta amb suport'; + + @override + String get emailYourLogs => 'Envia els teus registres per correu'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Si us plau, envia els registres a \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copia l\'adreça de correu'; + + @override + String get exportLogs => 'Exporta els registres'; + + @override + String get cancel => 'Cancel·la'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Informa d\'un error'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connectat a $endpoint'; + } + + @override + String get save => 'Guarda'; + + @override + String get send => 'Envia'; + + @override + String get saveOrSendDescription => + 'Vols guardar-ho al teu emmagatzematge (per defecte, a la carpeta Descàrregues) o enviar-ho a altres aplicacions?'; + + @override + String get saveOnlyDescription => + 'Vols guardar-ho al teu emmagatzematge (per defecte, a la carpeta Descàrregues)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Correu electrònic'; + + @override + String get verify => 'Verifica'; + + @override + String get invalidEmailTitle => 'Adreça de correu electrònic no vàlida'; + + @override + String get invalidEmailMessage => + 'Si us plau, introdueix una adreça de correu electrònic vàlida.'; + + @override + String get pleaseWait => 'Si us plau, espera...'; + + @override + String get verifyPassword => 'Verifica la contrasenya'; + + @override + String get incorrectPasswordTitle => 'Contrasenya incorrecta'; + + @override + String get pleaseTryAgain => 'Si us plau, intenta-ho de nou'; + + @override + String get enterPassword => 'Introdueix la contrasenya'; + + @override + String get enterYourPasswordHint => 'Introdueix la teva contrasenya'; + + @override + String get activeSessions => 'Sessions actives'; + + @override + String get oops => 'Ups'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'S\'ha produït un error, si us plau, intenta-ho de nou'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'Això tancarà la sessió en aquest dispositiu!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'Això tancarà la sessió en el següent dispositiu:'; + + @override + String get terminateSession => 'Finalitzar sessió?'; + + @override + String get terminate => 'Finalitzar'; + + @override + String get thisDevice => 'Aquest dispositiu'; + + @override + String get createAccount => 'Crea un compte'; + + @override + String get weakStrength => 'Feble'; + + @override + String get moderateStrength => 'Moderada'; + + @override + String get strongStrength => 'Forta'; + + @override + String get deleteAccount => 'Elimina el compte'; + + @override + String get deleteAccountQuery => + 'Ens sabrà greu veure\'t marxar. Tens algun problema?'; + + @override + String get yesSendFeedbackAction => 'Sí, envia comentaris'; + + @override + String get noDeleteAccountAction => 'No, elimina el compte'; + + @override + String get initiateAccountDeleteTitle => + 'Si us plau, autentica\'t per iniciar l\'eliminació del compte'; + + @override + String get confirmAccountDeleteTitle => 'Confirma la supressió del compte'; + + @override + String get confirmAccountDeleteMessage => + 'Aquest compte està vinculat a altres apps d\'Ente, si en fas ús.\n\nLes dades pujades, a través de totes les apps d\'Ente, es programaran per a la supressió, i el teu compte s\'eliminarà permanentment.'; + + @override + String get delete => 'Elimina'; + + @override + String get createNewAccount => 'Crea un nou compte'; + + @override + String get password => 'Contrasenya'; + + @override + String get confirmPassword => 'Confirma la contrasenya'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Força de la contrasenya: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'Com vas conèixer Ente? (opcional)'; + + @override + String get hearUsExplanation => + 'No fem seguiment de les instal·lacions de l\'app. Ens ajudaria saber on ens has trobat!'; + + @override + String get signUpTerms => + 'Estic d\'acord amb els termes del servei i la política de privacitat'; + + @override + String get termsOfServicesTitle => 'Termes'; + + @override + String get privacyPolicyTitle => 'Política de privacitat'; + + @override + String get ackPasswordLostWarning => + 'Entenc que si perdo la meva contrasenya, puc perdre les meves dades ja que les meves dades estan xifrades d\'extrem a extrem.'; + + @override + String get encryption => 'Xifratge'; + + @override + String get logInLabel => 'Inicia sessió'; + + @override + String get welcomeBack => 'Benvingut de nou!'; + + @override + String get loginTerms => + 'En fer clic a iniciar sessió, estic d\'acord amb els termes del servei i la política de privacitat'; + + @override + String get noInternetConnection => 'Sense connexió a Internet'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Comprova la connexió a Internet i torna-ho a intentar.'; + + @override + String get verificationFailedPleaseTryAgain => + 'La verificació ha fallat, intenta-ho de nou'; + + @override + String get recreatePasswordTitle => 'Recrea la contrasenya'; + + @override + String get recreatePasswordBody => + 'El dispositiu actual no és prou potent per verificar la teva contrasenya, però podem regenerar-la d\'una manera que funcioni amb tots els dispositius.\n\nSi us plau, inicia sessió utilitzant la teva clau de recuperació i regenera la teva contrasenya (pots tornar a utilitzar la mateixa si ho desitges).'; + + @override + String get useRecoveryKey => 'Usa la clau de recuperació'; + + @override + String get forgotPassword => 'Has oblidat la contrasenya'; + + @override + String get changeEmail => 'Canvia el correu electrònic'; + + @override + String get verifyEmail => 'Verifica el correu electrònic'; + + @override + String weHaveSendEmailTo(String email) { + return 'Hem enviat un correu a $email'; + } + + @override + String get toResetVerifyEmail => + 'Per restablir la teva contrasenya, si us plau verifica primer el teu correu electrònic.'; + + @override + String get checkInboxAndSpamFolder => + 'Comprova la teva safata d\'entrada (i el correu no desitjat) per completar la verificació'; + + @override + String get tapToEnterCode => 'Toca per introduir el codi'; + + @override + String get sendEmail => 'Envia correu electrònic'; + + @override + String get resendEmail => 'Reenviar correu electrònic'; + + @override + String get passKeyPendingVerification => 'La verificació encara està pendent'; + + @override + String get loginSessionExpired => 'Sessió caducada'; + + @override + String get loginSessionExpiredDetails => + 'La teva sessió ha caducat. Torna a iniciar sessió.'; + + @override + String get passkeyAuthTitle => 'Verificació per passkey'; + + @override + String get waitingForVerification => 'Esperant verificació...'; + + @override + String get tryAgain => 'Intenta-ho de nou'; + + @override + String get checkStatus => 'Comprova l\'estat'; + + @override + String get loginWithTOTP => 'Inici de sessió amb TOTP'; + + @override + String get recoverAccount => 'Recupera el compte'; + + @override + String get setPasswordTitle => 'Configura la contrasenya'; + + @override + String get changePasswordTitle => 'Canvia la contrasenya'; + + @override + String get resetPasswordTitle => 'Restableix la contrasenya'; + + @override + String get encryptionKeys => 'Claus de xifratge'; + + @override + String get enterPasswordToEncrypt => + 'Introdueix una contrasenya que puguem utilitzar per xifrar les teves dades'; + + @override + String get enterNewPasswordToEncrypt => + 'Introdueix una nova contrasenya que puguem utilitzar per xifrar les teves dades'; + + @override + String get passwordWarning => + 'No guardem aquesta contrasenya, per tant, si l\'oblides, no podrem desxifrar les teves dades'; + + @override + String get howItWorks => 'Com funciona'; + + @override + String get generatingEncryptionKeys => 'Generant claus de xifratge...'; + + @override + String get passwordChangedSuccessfully => + 'La contrasenya s\'ha canviat amb èxit'; + + @override + String get signOutFromOtherDevices => 'Tanca sessió en altres dispositius'; + + @override + String get signOutOtherBody => + 'Si creus que algú pot saber la teva contrasenya, pots forçar tots els altres dispositius que usen el teu compte a tancar sessió.'; + + @override + String get signOutOtherDevices => 'Tancar sessió en altres dispositius'; + + @override + String get doNotSignOut => 'No tancar sessió'; + + @override + String get generatingEncryptionKeysTitle => + 'Generant claus d\'encriptació...'; + + @override + String get continueLabel => 'Continua'; + + @override + String get insecureDevice => 'Dispositiu no segur'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Ho sentim, no hem pogut generar claus segures en aquest dispositiu.\n\nSi us plau, registra\'t des d\'un altre dispositiu.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'La clau de recuperació s\'ha copiat al porta-retalls'; + + @override + String get recoveryKey => 'Clau de recuperació'; + + @override + String get recoveryKeyOnForgotPassword => + 'Si oblides la teva contrasenya, l\'única manera de recuperar les teves dades és amb aquesta clau.'; + + @override + String get recoveryKeySaveDescription => + 'No guardem aquesta clau, si us plau, guarda aquesta clau de 24 paraules en un lloc segur.'; + + @override + String get doThisLater => 'Fes-ho més tard'; + + @override + String get saveKey => 'Guarda la clau'; + + @override + String get recoveryKeySaved => + 'Clau de recuperació guardada a la carpeta Descàrregues!'; + + @override + String get noRecoveryKeyTitle => 'No tens clau de recuperació?'; + + @override + String get twoFactorAuthTitle => 'Autenticació de dos factors'; + + @override + String get enterCodeHint => + 'Introdueix el codi de 6 dígits de\nl\'aplicació d\'autenticació'; + + @override + String get lostDeviceTitle => 'Dispositiu perdut?'; + + @override + String get enterRecoveryKeyHint => 'Introdueix la teva clau de recuperació'; + + @override + String get recover => 'Recupera'; + + @override + String get loggingOut => 'Tancant sessió...'; + + @override + String get immediately => 'Immediatament'; + + @override + String get appLock => 'Bloqueig de l\'aplicació'; + + @override + String get autoLock => 'Bloqueig automàtic'; + + @override + String get noSystemLockFound => 'No s\'ha trobat cap bloqueig del sistema'; + + @override + String get deviceLockEnablePreSteps => + 'Per habilitar el bloqueig de dispositiu, configura un codi o bloqueig de pantalla en la configuració del sistema.'; + + @override + String get appLockDescription => + 'Tria entre el bloqueig predeterminat del dispositiu o un bloqueig personalitzat amb PIN o contrasenya.'; + + @override + String get deviceLock => 'Bloqueig del dispositiu'; + + @override + String get pinLock => 'Bloqueig amb PIN'; + + @override + String get autoLockFeatureDescription => + 'Temps després del qual l\'app es bloqueja quan es posa en segon pla'; + + @override + String get hideContent => 'Amaga el contingut'; + + @override + String get hideContentDescriptionAndroid => + 'Amaga el contingut d\'aquesta app en el commutador d\'apps del sistema i desactiva les captures de pantalla'; + + @override + String get hideContentDescriptioniOS => + 'Amaga el contingut d\'aquesta app en el commutador d\'apps del sistema'; + + @override + String get tooManyIncorrectAttempts => 'Massa intents incorrectes'; + + @override + String get tapToUnlock => 'Toca per desbloquejar'; + + @override + String get areYouSureYouWantToLogout => 'Segur que vols tancar la sessió?'; + + @override + String get yesLogout => 'Sí, tanca la sessió'; + + @override + String get authToViewSecrets => + 'Si us plau, autentica\'t per veure els teus secrets'; + + @override + String get next => 'Següent'; + + @override + String get setNewPassword => 'Estableix una nova contrasenya'; + + @override + String get enterPin => 'Introdueix el PIN'; + + @override + String get setNewPin => 'Estableix un nou PIN'; + + @override + String get confirm => 'Confirma'; + + @override + String get reEnterPassword => 'Torna a introduir la contrasenya'; + + @override + String get reEnterPin => 'Torna a introduir el PIN'; + + @override + String get androidBiometricHint => 'Verifica la identitat'; + + @override + String get androidBiometricNotRecognized => + 'No reconegut. Torna-ho a provar.'; + + @override + String get androidBiometricSuccess => 'Correcte'; + + @override + String get androidCancelButton => 'Cancel·la'; + + @override + String get androidSignInTitle => 'Es requereix autenticació'; + + @override + String get androidBiometricRequiredTitle => 'Biometria necessària'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Credencials del dispositiu requerides'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Es requereixen credencials del dispositiu'; + + @override + String get goToSettings => 'Ves a configuració'; + + @override + String get androidGoToSettingsDescription => + 'L\'autenticació biomètrica no està configurada al teu dispositiu. Ves a \'Configuració > Seguretat\' per afegir autenticació biomètrica.'; + + @override + String get iOSLockOut => + 'L\'autenticació biomètrica està desactivada. Bloqueja i desbloqueja la pantalla per activar-la.'; + + @override + String get iOSOkButton => 'D\'acord'; + + @override + String get emailAlreadyRegistered => + 'El correu electrònic ja està registrat.'; + + @override + String get emailNotRegistered => 'El correu electrònic no està registrat.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Aquest correu electrònic ja està en ús'; + + @override + String emailChangedTo(String newEmail) { + return 'Correu electrònic canviat a $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Autenticació fallida, intenta-ho de nou'; + + @override + String get authenticationSuccessful => 'Autenticació amb èxit!'; + + @override + String get sessionExpired => 'La sessió ha caducat'; + + @override + String get incorrectRecoveryKey => 'Clau de recuperació incorrecta'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'La clau de recuperació que has introduït és incorrecta'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Autenticació de dos factors restablerta amb èxit'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'El teu codi de verificació ha expirat'; + + @override + String get incorrectCode => 'Codi incorrecte'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Ho sentim, el codi que has introduït és incorrecte'; + + @override + String get developerSettings => 'Configuració de desenvolupador'; + + @override + String get serverEndpoint => 'Endpoint del servidor'; + + @override + String get invalidEndpoint => 'Endpoint no vàlid'; + + @override + String get invalidEndpointMessage => + 'Ho sentim, l\'endpoint que has introduït no és vàlid. Introdueix un endpoint vàlid i torna-ho a intentar.'; + + @override + String get endpointUpdatedMessage => 'Extrem actualitzat correctament'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index 09e0bc5800..f72957d5d5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -14,40 +14,40 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Nepodařilo se připojit k Ente, zkuste to po nějaké době znovu. Pokud chyba přetrvává, kontaktujte, prosím, podporu.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Vypadá to, že se něco pokazilo. Zkuste to prosím znovu po nějaké době. Pokud chyba přetrvává, kontaktujte prosím naši podporu.'; @override - String get error => 'Error'; + String get error => 'Chyba'; @override String get ok => 'Ok'; @override - String get faq => 'FAQ'; + String get faq => 'Často kladené dotazy (FAQ)'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Kontaktovat podporu'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Zašlete své logy e-mailem'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Pošlete prosím logy na \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Kopírovat e-mailovou adresu'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exportovat logy'; @override - String get cancel => 'Cancel'; + String get cancel => 'Zrušit'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsCs extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Nahlásit chybu'; @override String get logsDialogBody => @@ -100,505 +88,507 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Připojeno k $endpoint'; } @override - String get save => 'Save'; + String get save => 'Uložit'; @override - String get send => 'Send'; + String get send => 'Odeslat'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory), nebo odeslat do jiných aplikací?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Ověřit'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Neplatná e-mailová adresa'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Prosím, zadejte platnou e-mailovou adresu.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Čekejte prosím...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Ověření hesla'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nesprávné heslo'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Zkuste to prosím znovu'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Zadejte heslo'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Zadejte své heslo'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktivní relace'; @override - String get oops => 'Oops'; + String get oops => 'Jejda'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Něco se pokazilo. Zkuste to, prosím, znovu'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Tato akce Vás odhlásí z tohoto zařízení!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Toto Vás odhlásí z následujícího zařízení:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Ukončit relaci?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Ukončit'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Toto zařízení'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Vytvořit účet'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Slabé'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Střední'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silné'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Odstranit účet'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Mrzí nás, že odcházíte. Máte nějaké problémy s aplikací?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ano, poslat zpětnou vazbu'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Ne, odstranit účet'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Pro zahájení odstranění účtu se, prosím, ověřte'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potvrdit odstranění účtu'; @override - String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + String get confirmAccountDeleteMessage => ' '; @override - String get delete => 'Delete'; + String get delete => 'Smazat'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Vytvořit nový účet'; @override - String get password => 'Password'; + String get password => 'Heslo'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potvrzení hesla'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Síla hesla: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Jak jste se dozvěděli o Ente? (volitelné)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Ne sledujeme instalace aplikace. Pomůže nám, když nám sdělíte, kde jste nás našli!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Podmínky'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Podmínky ochrany osobních údajů'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumím, že při zapomenutí hesla mohu ztratit svá data, protože jsou zabezpečena koncovým šifrováním.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifrování'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Přihlásit se'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Vítejte zpět!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Kliknutím na přihlášení souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Žádné připojení k internetu'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Zkontrolujte, prosím, své připojení k internetu a zkuste to znovu.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Ověření selhalo, přihlaste se, prosím, znovu'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Resetovat heslo'; @override String get recreatePasswordBody => - '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).'; + 'Aktzální zařízení není dostatečně výkonné pro ověření Vašeho hesla, ale můžeme ho regenerovat způsobem, který funguje ve všech zařízením.\n\nPřihlašte se pomocí obnovovacího klíče a znovu si vygenerujte své heslo (můžete použít opět stejné, pokud chcete).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Použít obnovovací klíč'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Zapomenuté heslo'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Změnit e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Ověřit e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Odeslali jsme e-mail na $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Pro obnovení hesla obnovte, prosím, nejprve svůj e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Pro dokončení ověření prosím zkontrolujte, prosím, svou doručenou poštu (a spamy)'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Klepnutím zadejte kód'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Odeslat e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Odeslat e-mail znovu'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Ověřování stále probíhá'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Relace vypršela'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Vaše relace vypršela. Přihlaste se, prosím, znovu.'; @override String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Čekání na ověření...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Zkusit znovu'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Zkontrolovat stav'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Přihlášení s TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Obnovit účet'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nastavit heslo'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Změnit heslo'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Obnovit heslo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifrovací klíče'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Zadejte heslo, kterým můžeme zašifrovat Vaše data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Zadejte nové heslo, kterým můžeme šifrovat Vaše data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vaše heslo neuchováváme. Pokud ho zapomenete, nemůžeme Vaše data dešifrovat'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Jak to funguje'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generování šifrovacích klíčů...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Heslo úspěšně změněno'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Odhlásit z ostatních zařízení'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Pokud si myslíte, že by někdo mohl znát Vaše heslo, můžete vynutit odhlášení ostatních zařízení používajících Váš účet.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Odhlásit z ostatních zařízení'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neodhlašovat'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generování šifrovacích klíčů...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Pokračovat'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Nezabezpečené zařízení'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Omlouváme se, na tomto zařízení nemůžeme vygenerovat bezpečné klíče.\n\nprosím přihlaste se z jiného zařízení.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => 'Obnovovací klíč byl zkopírován'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Obnovovací klíč'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Tento klíč je jedinou cestou pro obnovení Vašich dat, pokud zapomenete heslo.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Tento 24místný klíč neuchováváme, uschovejte ho, prosím, na bezpečném místě.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Udělat později'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Uložit klíč'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Obnovovací klíč uložen do složky Stažené soubory!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Nemáte obnovovací klíč?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvoufaktorové ověření'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Zadejte 6místný kód ze své autentizační aplikace'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Ztratili jste zařízení?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Zadejte svůj obnovovací klíč'; @override - String get recover => 'Recover'; + String get recover => 'Obnovit'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Odhlašování...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Ihned'; @override - String get appLock => 'App lock'; + String get appLock => 'Zámek aplikace'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatické zamykání'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Zámek systému nenalezen'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Pro aktivaci zámku zařízení si nastavte přístupový kód zařízení nebo zámek obrazovky v nastavení systému.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Vyberte si mezi zámkem obrazovky svého zařízení a vlastním zámkem obrazovky s PIN kódem nebo heslem.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Zámek zařízení'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Uzamčení na PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Interval, po kterém se aplikace běžící na pozadí uzamkne'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Skrýt obsah'; @override - String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + String get hideContentDescriptionAndroid => 'Skryje obsah aplikace ve '; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Skryje obsah aplikace při přepínání úloh'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Příliš mnoho neúspěšných pokusů'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Pro odemčení klepněte'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Opravdu se chcete odhlásit?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ano, odhlásit se'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Pro zobrazení svých tajných údajů se musíte ověřit'; @override - String get next => 'Next'; + String get next => 'Další'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Nastavit nové heslo'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Zadejte PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Nadra'; @override - String get confirm => 'Confirm'; + String get confirm => 'Potvrdit'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Zadejte heslo znovu'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Zadejte PIN znovu'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Ověřte svou identitu'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Nerozpoznáno. Zkuste znovu.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Úspěch'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Zrušit'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Je požadováno ověření'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => + 'Je požadováno biometrické ověření'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Jsou vyžadovány přihlašovací údaje zařízení'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Jsou vyžadovány přihlašovací údaje zařízení'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Jít do nastavení'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Na Vašem zařízení není nastaveno biometrické ověřování. Pro aktivaci běžte do \'Nastavení > Zabezpečení\'.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biometrické ověřování není povoleno. Pro povolení zamkněte a odemkněte obrazovku.'; @override String get iOSOkButton => 'OK'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-mail je již registrován.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-mail není registrován.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Tento e-mail je již používán'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'E-mail změněn na $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Ověření selhalo, zkuste to, prosím, znovu'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Ověření bylo úspěšné!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Relace vypršela'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Nesprávný obnovovací klíč'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Vámi zadaný obnovovací klíč je nesprávný'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Dvoufázové ověření bylo úspěšně obnoveno'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +600,28 @@ class StringsLocalizationsCs extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => 'Váš ověřovací kód vypršel'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Nesprávný kód'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Omlouváme se, zadaný kód je nesprávný'; + + @override + String get developerSettings => 'Nastavení pro vývojáře'; + + @override + String get serverEndpoint => 'Koncový bod serveru'; + + @override + String get invalidEndpoint => 'Neplatný koncový bod'; + + @override + String get invalidEndpointMessage => + 'Zadaný koncový bod je neplatný. Zadejte prosím platný koncový bod a zkuste to znovu.'; + + @override + String get endpointUpdatedMessage => 'Koncový bod byl úspěšně aktualizován'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index ed28c5db33..82f2d03070 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -14,40 +14,40 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Ude af stand til at forbinde til Ente. Forsøg igen efter et stykke tid. Hvis fejlen varer ved, kontakt da venligst support.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Det ser ud til at noget gik galt. Forsøg venligst igen efter lidt tid. Hvis fejlen varer ved, kontakt da venligst support.'; @override - String get error => 'Error'; + String get error => 'Fejl'; @override - String get ok => 'Ok'; + String get ok => 'OK'; @override String get faq => 'FAQ'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Kontakt support'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Email dine logs'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Send venligst logs til $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Kopier email adresse'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Eksporter logs'; @override - String get cancel => 'Cancel'; + String get cancel => 'Afbryd'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsDa extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Rapporter en fejl'; @override String get logsDialogBody => @@ -100,22 +88,22 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Forbindelse oprettet til $endpoint'; } @override - String get save => 'Save'; + String get save => 'Gem'; @override String get send => 'Send'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Vil du gemme på din enhed (Downloads mappe som udgangspunkt) eller sende til andre apps?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Vil du gemme på din enhed (Downloads mappe som udgangspunkt)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -124,443 +112,445 @@ class StringsLocalizationsDa extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Bekræft'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ugyldig email adresse'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Indtast en gyldig email adresse.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vent venligst...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bekræft adgangskode'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Forkert adgangskode'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Forsøg venligst igen'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Indtast adgangskode'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Indtast adgangskode'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktive sessioner'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Noget gik galt, forsøg venligst igen'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Dette vil logge dig ud af denne enhed!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Dette vil logge dig ud af den følgende enhed:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Afslut session?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Afslut'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Denne enhed'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Opret konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Svagt'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Middel'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stærkt'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Slet konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Vi er kede af at se dig gå. Er du stødt på et problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, send feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nej, slet konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Bekræft venligst for at påbegynde sletning af konto'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Bekræft sletning af konto'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Denne konto er forbundet til andre Ente apps, hvis du benytter nogle.\n\nDine uploadede data for alle Ente apps vil blive slettet, og din konto vil blive slettet permanent.'; @override - String get delete => 'Delete'; + String get delete => 'Slet'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Opret konto'; @override - String get password => 'Password'; + String get password => 'Kodeord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Bekræft kodeord'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Kodeordets styrke: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hvordan hørte du om Ente? (valgfrit)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Vi tracker ikke app installeringer. Det ville hjælpe os at vide hvordan du fandt os!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Jeg er enig i betingelser for brug og privatlivspolitik'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Betingelser'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privatlivspolitik'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Jeg forstår at hvis jeg mister min adgangskode kan jeg miste mine data, da mine data er end-to-end krypteret.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kryptering'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Log ind'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Velkommen tilbage!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Ved at logge ind godkender jeg Ente\'s betingelser for brug og privatlivspolitik.'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Ingen internetforbindelse'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Tjek venligst din internetforbindelse og forsøg igen.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Bekræftelse fejlede, forsøg venligst igen'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Gendan adgangskode'; @override String get recreatePasswordBody => - '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).'; + 'Denne enhed er ikke kraftfuld nok til at bekræfte adgangskoden, men vi kan gendanne den på en måde der fungerer for alle enheder.\n\nLog venligst ind med din gendannelsesnøgle og gendan din adgangskode (du kan bruge den samme adgangskode igen hvis du ønsker).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Brug gendannelsesnøgle'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Glemt adgangskode'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Skift email adresse'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Bekræft email adresse'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Vi har sendt en email til $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'For at nulstille din adgangskode, bekræft venligst din email adresse.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Tjek venligst din indboks (og spam) for at færdiggøre verificeringen'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tryk for at indtaste kode'; @override String get sendEmail => 'Send email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Send email igen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Bekræftelse afventes stadig'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Session udløbet'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Din session er udløbet. Log venligst på igen.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Bekræftelse af adgangskode'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Venter på bekræftelse...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Forsøg igen'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Tjek status'; @override String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Gendan konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Angiv adgangskode'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Skift adgangskode'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Nulstil adgangskode'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Krypteringsnøgler'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Indtast en adgangskode vi kan bruge til at kryptere dine data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Indtast en ny adgangskode vi kan bruge til at kryptere dine data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vi gemmer ikke denne adgangskode, så hvis du glemmer den kan vi ikke dekryptere dine data'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Sådan fungerer det'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Genererer krypteringsnøgler...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Adgangskoden er blevet ændret'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Log ud af andre enheder'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Hvis du mistænker at nogen kender din adgangskode kan du tvinge alle enheder der benytter din konto til at logge ud.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Log ud af andre enheder'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Log ikke ud'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Genererer krypteringsnøgler...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Fortsæt'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Usikker enhed'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Beklager, vi kunne ikke generere sikre krypteringsnøgler på denne enhed.\n\nForsøg venligst at oprette en konto fra en anden enhed.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Gendannelsesnøgle kopieret til udklipsholderen'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Gendannelsesnøgle'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Hvis du glemmer dit kodeord er gendannelsesnøglen den eneste mulighed for at få adgang til dine data.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Vi gemmer ikke denne nøgle, gem venligst denne 24-ords nøgle et sikkert sted.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Gør det senere'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Gem nøgle'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Gendannelsesnøgle gemt i din Downloads mappe!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Ingen gendannelsesnøgle?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tofaktorgodkendelse'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Indtast den 6-cifrede kode fra din authenticator app'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Mistet enhed?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Indtast din gendannelsesnøgle'; @override - String get recover => 'Recover'; + String get recover => 'Gendan'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Logger ud...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Med det samme'; @override - String get appLock => 'App lock'; + String get appLock => 'Låsning af app'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatisk lås'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Ingen systemlås fundet'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'For at aktivere enhedslås, indstil venligst kode eller skærmlås på din enhed i dine systemindstillinger.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Vælg mellem din enheds standard skærmlås eller skærmlås med pinkode eller adgangskode.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Enhedslås'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Låsning med pinkode'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Tid til låsning af app efter at være blevet placeret i baggrunden'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Skjul indhold'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Skjul app indhold i app-vælger og deaktiver screenshots'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'Skjul app indhold i app-vælger'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'For mange forkerte forsøg'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Tryk for at låse op'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Er du sikker på at du vil logge ud?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ja, log ud'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Bekræft venligst din identitet for at se dine hemmeligheder'; @override - String get next => 'Next'; + String get next => 'Næste'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Indstil ny adgangskode'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Indtast pinkode'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Indstil ny pinkode'; @override - String get confirm => 'Confirm'; + String get confirm => 'Bekræft'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Indtast adgangskode igen'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Indtast pinkode igen'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Bekræft identitet'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Ikke genkendt. Forsøg igen.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Succes'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Afbryd'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Godkendelse påkrævet'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Biometri påkrævet'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Enhedsoplysninger påkrævet'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Enhedsoplysninger påkrævet'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Gå til indstillinger'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Biometrisk godkendelse er ikke indstillet på din enhed. Gå til \"Indstillinger > Sikkerhed\" for at indstille biometrisk godkendelse.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biometrisk godkendelse er slået fra. Lås din skærm, og lås den derefter op for at aktivere det.'; @override String get iOSOkButton => 'OK'; @@ -572,33 +562,34 @@ class StringsLocalizationsDa extends StringsLocalizations { String get emailNotRegistered => 'Email not registered.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => + 'Denne email adresse er allerede i brug'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Email adresse ændret til $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Bekræftelse af identitet fejlede, forsøg venligst igen'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Bekræftelse af identitet lykkedes!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Session udløbet'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Forkert gendannelsesnøgle'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Den indtastede gendannelsesnøgle er ikke korrekt'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Tofaktorgodkendelse nulstillet'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +602,28 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Din bekræftelseskode er udløbet'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Forkert kode'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Beklager, den indtastede kode er forkert'; + + @override + String get developerSettings => 'Udvikler-indstillinger'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Ugyldigt endpoint'; + + @override + String get invalidEndpointMessage => + 'Beklager, det indtastede endpoint er ugyldigt. Indtast venligst et gyldigt endpoint og forsøg igen.'; + + @override + String get endpointUpdatedMessage => 'Endpoint opdateret'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_de.dart b/mobile/packages/strings/lib/l10n/strings_localizations_de.dart new file mode 100644 index 0000000000..3bf0aca783 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_de.dart @@ -0,0 +1,636 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class StringsLocalizationsDe extends StringsLocalizations { + StringsLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Ente ist im Moment nicht erreichbar. Bitte überprüfen Sie Ihre Netzwerkeinstellungen. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.'; + + @override + String get networkConnectionRefusedErr => + 'Ente ist im Moment nicht erreichbar. Bitte versuchen Sie es später erneut. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Etwas ist schiefgelaufen. Bitte versuchen Sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren Sie unser Supportteam.'; + + @override + String get error => 'Fehler'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Support kontaktieren'; + + @override + String get emailYourLogs => 'E-Mail mit Logs senden'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Bitte Logs an $toEmail senden'; + } + + @override + String get copyEmailAddress => 'E-Mail-Adresse kopieren'; + + @override + String get exportLogs => 'Logs exportieren'; + + @override + String get cancel => 'Abbrechen'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Einen Fehler melden'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Mit $endpoint verbunden'; + } + + @override + String get save => 'Speichern'; + + @override + String get send => 'Senden'; + + @override + String get saveOrSendDescription => + 'Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern oder an andere Apps senden?'; + + @override + String get saveOnlyDescription => + 'Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern?'; + + @override + String get enterNewEmailHint => 'Gib deine neue E-Mail-Adresse ein'; + + @override + String get email => 'E-Mail'; + + @override + String get verify => 'Verifizieren'; + + @override + String get invalidEmailTitle => 'Ungültige E-Mail-Adresse'; + + @override + String get invalidEmailMessage => + 'Bitte geben Sie eine gültige E-Mail-Adresse ein.'; + + @override + String get pleaseWait => 'Bitte warten...'; + + @override + String get verifyPassword => 'Passwort überprüfen'; + + @override + String get incorrectPasswordTitle => 'Falsches Passwort'; + + @override + String get pleaseTryAgain => 'Bitte versuchen Sie es erneut'; + + @override + String get enterPassword => 'Passwort eingeben'; + + @override + String get enterYourPasswordHint => 'Geben Sie Ihr Passwort ein'; + + @override + String get activeSessions => 'Aktive Sitzungen'; + + @override + String get oops => 'Hoppla'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Ein Fehler ist aufgetreten, bitte erneut versuchen'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'Dadurch werden Sie von diesem Gerät abgemeldet!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'Dadurch werden Sie vom folgendem Gerät abgemeldet:'; + + @override + String get terminateSession => 'Sitzung beenden?'; + + @override + String get terminate => 'Beenden'; + + @override + String get thisDevice => 'Dieses Gerät'; + + @override + String get createAccount => 'Konto erstellen'; + + @override + String get weakStrength => 'Schwach'; + + @override + String get moderateStrength => 'Mittel'; + + @override + String get strongStrength => 'Stark'; + + @override + String get deleteAccount => 'Konto löschen'; + + @override + String get deleteAccountQuery => + 'Es tut uns leid, dass Sie gehen. Haben Sie ein Problem?'; + + @override + String get yesSendFeedbackAction => 'Ja, Feedback senden'; + + @override + String get noDeleteAccountAction => 'Nein, Konto löschen'; + + @override + String get initiateAccountDeleteTitle => + 'Bitte authentifizieren Sie sich, um die Kontolöschung einzuleiten'; + + @override + String get confirmAccountDeleteTitle => 'Kontolöschung bestätigen'; + + @override + String get confirmAccountDeleteMessage => + 'Dieses Konto ist mit anderen Ente-Apps verknüpft, falls Sie welche verwenden.\n\nIhre hochgeladenen Daten werden in allen Ente-Apps zur Löschung vorgemerkt und Ihr Konto wird endgültig gelöscht.'; + + @override + String get delete => 'Löschen'; + + @override + String get createNewAccount => 'Neues Konto erstellen'; + + @override + String get password => 'Passwort'; + + @override + String get confirmPassword => 'Bestätigen Sie das Passwort'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Passwortstärke: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'Wie hast du von Ente erfahren? (optional)'; + + @override + String get hearUsExplanation => + 'Wir tracken keine App-Installationen. Es würde uns jedoch helfen, wenn du uns mitteilst, wie du von uns erfahren hast!'; + + @override + String get signUpTerms => + 'Ich stimme den Nutzerbedingungen und Datenschutzbestimmungen zu'; + + @override + String get termsOfServicesTitle => 'Bedingungen'; + + @override + String get privacyPolicyTitle => 'Datenschutzbestimmungen'; + + @override + String get ackPasswordLostWarning => + 'Ich verstehe, dass der Verlust meines Passworts zum Verlust meiner Daten führen kann, denn diese sind Ende-zu-Ende verschlüsselt.'; + + @override + String get encryption => 'Verschlüsselung'; + + @override + String get logInLabel => 'Einloggen'; + + @override + String get welcomeBack => 'Willkommen zurück!'; + + @override + String get loginTerms => + 'Durch das Klicken auf den Login-Button, stimme ich den Nutzungsbedingungen und den Datenschutzbestimmungen zu'; + + @override + String get noInternetConnection => 'Keine Internetverbindung'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie erneut.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verifizierung fehlgeschlagen, bitte versuchen Sie es erneut'; + + @override + String get recreatePasswordTitle => 'Neues Passwort erstellen'; + + @override + String get recreatePasswordBody => + 'Das benutzte Gerät ist nicht leistungsfähig genug das Passwort zu prüfen. Wir können es aber neu erstellen damit es auf jedem Gerät funktioniert. \n\nBitte loggen sie sich mit ihrem Wiederherstellungsschlüssel ein und erstellen sie ein neues Passwort (Sie können das selbe Passwort wieder verwenden, wenn sie möchten).'; + + @override + String get useRecoveryKey => 'Wiederherstellungsschlüssel verwenden'; + + @override + String get forgotPassword => 'Passwort vergessen'; + + @override + String get changeEmail => 'E-Mail ändern'; + + @override + String get verifyEmail => 'E-Mail-Adresse verifizieren'; + + @override + String weHaveSendEmailTo(String email) { + return 'Wir haben eine E-Mail an $email gesendet'; + } + + @override + String get toResetVerifyEmail => + 'Um Ihr Passwort zurückzusetzen, verifizieren Sie bitte zuerst Ihre E-Mail-Adresse.'; + + @override + String get checkInboxAndSpamFolder => + 'Bitte überprüfe deinen E-Mail-Posteingang (und Spam), um die Verifizierung abzuschließen'; + + @override + String get tapToEnterCode => 'Antippen, um den Code einzugeben'; + + @override + String get sendEmail => 'E-Mail senden'; + + @override + String get resendEmail => 'E-Mail erneut senden'; + + @override + String get passKeyPendingVerification => 'Verifizierung steht noch aus'; + + @override + String get loginSessionExpired => 'Sitzung abgelaufen'; + + @override + String get loginSessionExpiredDetails => + 'Ihre Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.'; + + @override + String get passkeyAuthTitle => 'Passkey Authentifizierung'; + + @override + String get waitingForVerification => 'Warte auf Bestätigung...'; + + @override + String get tryAgain => 'Noch einmal versuchen'; + + @override + String get checkStatus => 'Status überprüfen'; + + @override + String get loginWithTOTP => 'Mit TOTP anmelden'; + + @override + String get recoverAccount => 'Konto wiederherstellen'; + + @override + String get setPasswordTitle => 'Passwort setzen'; + + @override + String get changePasswordTitle => 'Passwort ändern'; + + @override + String get resetPasswordTitle => 'Passwort zurücksetzen'; + + @override + String get encryptionKeys => 'Verschlüsselungsschlüssel'; + + @override + String get enterPasswordToEncrypt => + 'Geben Sie ein Passwort ein, mit dem wir Ihre Daten verschlüsseln können'; + + @override + String get enterNewPasswordToEncrypt => + 'Geben Sie ein neues Passwort ein, mit dem wir Ihre Daten verschlüsseln können'; + + @override + String get passwordWarning => + 'Wir speichern dieses Passwort nicht. Wenn Sie es vergessen, können wir Ihre Daten nicht entschlüsseln'; + + @override + String get howItWorks => 'So funktioniert\'s'; + + @override + String get generatingEncryptionKeys => + 'Generierung von Verschlüsselungsschlüsseln...'; + + @override + String get passwordChangedSuccessfully => 'Passwort erfolgreich geändert'; + + @override + String get signOutFromOtherDevices => 'Von anderen Geräten abmelden'; + + @override + String get signOutOtherBody => + 'Falls Sie denken, dass jemand Ihr Passwort kennen könnte, können Sie alle anderen Geräte forcieren, sich von Ihrem Konto abzumelden.'; + + @override + String get signOutOtherDevices => 'Andere Geräte abmelden'; + + @override + String get doNotSignOut => 'Nicht abmelden'; + + @override + String get generatingEncryptionKeysTitle => + 'Generierung von Verschlüsselungsschlüsseln...'; + + @override + String get continueLabel => 'Weiter'; + + @override + String get insecureDevice => 'Unsicheres Gerät'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Es tut uns leid, wir konnten keine sicheren Schlüssel auf diesem Gerät generieren.\n\nBitte registrieren Sie sich auf einem anderen Gerät.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Wiederherstellungsschlüssel in die Zwischenablage kopiert'; + + @override + String get recoveryKey => 'Wiederherstellungsschlüssel'; + + @override + String get recoveryKeyOnForgotPassword => + 'Sollten sie ihr Passwort vergessen, dann ist dieser Schlüssel die einzige Möglichkeit ihre Daten wiederherzustellen.'; + + @override + String get recoveryKeySaveDescription => + 'Wir speichern diesen Schlüssel nicht. Sichern Sie diesen Schlüssel bestehend aus 24 Wörtern an einem sicheren Platz.'; + + @override + String get doThisLater => 'Auf später verschieben'; + + @override + String get saveKey => 'Schlüssel speichern'; + + @override + String get recoveryKeySaved => + 'Wiederherstellungsschlüssel im Downloads-Ordner gespeichert!'; + + @override + String get noRecoveryKeyTitle => 'Kein Wiederherstellungsschlüssel?'; + + @override + String get twoFactorAuthTitle => 'Zwei-Faktor-Authentifizierung'; + + @override + String get enterCodeHint => + 'Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.'; + + @override + String get lostDeviceTitle => 'Gerät verloren?'; + + @override + String get enterRecoveryKeyHint => + 'Geben Sie Ihren Wiederherstellungsschlüssel ein'; + + @override + String get recover => 'Wiederherstellen'; + + @override + String get loggingOut => 'Wird abgemeldet...'; + + @override + String get immediately => 'Sofort'; + + @override + String get appLock => 'App-Sperre'; + + @override + String get autoLock => 'Automatisches Sperren'; + + @override + String get noSystemLockFound => 'Keine Systemsperre gefunden'; + + @override + String get deviceLockEnablePreSteps => + 'Um die Gerätesperre zu aktivieren, richte bitte einen Gerätepasscode oder eine Bildschirmsperre in den Systemeinstellungen ein.'; + + @override + String get appLockDescription => + 'Wähle zwischen dem Standard-Sperrbildschirm deines Gerätes und einem eigenen Sperrbildschirm mit PIN oder Passwort.'; + + @override + String get deviceLock => 'Gerätesperre'; + + @override + String get pinLock => 'PIN-Sperre'; + + @override + String get autoLockFeatureDescription => + 'Zeit, nach der die App gesperrt wird, nachdem sie in den Hintergrund verschoben wurde'; + + @override + String get hideContent => 'Inhalte verstecken'; + + @override + String get hideContentDescriptionAndroid => + 'Versteckt Inhalte der App beim Wechseln zwischen Apps und deaktiviert Screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Versteckt Inhalte der App beim Wechseln zwischen Apps'; + + @override + String get tooManyIncorrectAttempts => 'Zu viele fehlerhafte Versuche'; + + @override + String get tapToUnlock => 'Zum Entsperren antippen'; + + @override + String get areYouSureYouWantToLogout => + 'Sind sie sicher, dass sie sich ausloggen möchten?'; + + @override + String get yesLogout => 'Ja ausloggen'; + + @override + String get authToViewSecrets => + 'Bitte authentifizieren, um Ihren Wiederherstellungscode anzuzeigen'; + + @override + String get next => 'Weiter'; + + @override + String get setNewPassword => 'Neues Passwort festlegen'; + + @override + String get enterPin => 'PIN eingeben'; + + @override + String get setNewPin => 'Neue PIN festlegen'; + + @override + String get confirm => 'Bestätigen'; + + @override + String get reEnterPassword => 'Passwort erneut eingeben'; + + @override + String get reEnterPin => 'PIN erneut eingeben'; + + @override + String get androidBiometricHint => 'Identität bestätigen'; + + @override + String get androidBiometricNotRecognized => + 'Nicht erkannt. Versuchen Sie es erneut.'; + + @override + String get androidBiometricSuccess => 'Erfolgreich'; + + @override + String get androidCancelButton => 'Abbrechen'; + + @override + String get androidSignInTitle => 'Authentifizierung erforderlich'; + + @override + String get androidBiometricRequiredTitle => 'Biometrie erforderlich'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Geräteanmeldeinformationen erforderlich'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Geräteanmeldeinformationen erforderlich'; + + @override + String get goToSettings => 'Zu den Einstellungen'; + + @override + String get androidGoToSettingsDescription => + 'Auf Ihrem Gerät ist keine biometrische Authentifizierung eingerichtet. Gehen Sie zu \'Einstellungen > Sicherheit\', um die biometrische Authentifizierung hinzuzufügen.'; + + @override + String get iOSLockOut => + 'Die biometrische Authentifizierung ist deaktiviert. Bitte sperren und entsperren Sie Ihren Bildschirm, um sie wieder zu aktivieren.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'E-Mail ist bereits registriert.'; + + @override + String get emailNotRegistered => 'E-Mail-Adresse nicht registriert.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Diese E-Mail-Adresse wird bereits verwendet'; + + @override + String emailChangedTo(String newEmail) { + return 'E-Mail-Adresse geändert zu $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentifizierung fehlgeschlagen, bitte erneut versuchen'; + + @override + String get authenticationSuccessful => 'Authentifizierung erfolgreich!'; + + @override + String get sessionExpired => 'Sitzung abgelaufen'; + + @override + String get incorrectRecoveryKey => 'Falscher Wiederherstellungs-Schlüssel'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Der eingegebene Wiederherstellungs-Schlüssel ist ungültig'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Zwei-Faktor-Authentifizierung (2FA) erfolgreich zurückgesetzt'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Ihr Bestätigungscode ist abgelaufen'; + + @override + String get incorrectCode => 'Falscher Code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Leider ist der eingegebene Code falsch'; + + @override + String get developerSettings => 'Entwicklereinstellungen'; + + @override + String get serverEndpoint => 'Server Endpunkt'; + + @override + String get invalidEndpoint => 'Ungültiger Endpunkt'; + + @override + String get invalidEndpointMessage => + 'Der eingegebene Endpunkt ist ungültig. Bitte geben Sie einen gültigen Endpunkt ein und versuchen Sie es erneut.'; + + @override + String get endpointUpdatedMessage => 'Endpunkt erfolgreich aktualisiert'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 605e90acd3..f2af902f83 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -14,40 +14,40 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Δεν είναι δυνατή η σύνδεση με το Ente, παρακαλώ προσπαθήστε ξανά μετά από λίγο. Εάν το σφάλμα παραμένει, παρακαλούμε επικοινωνήστε με την υποστήριξη.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Φαίνεται ότι κάτι πήγε στραβά. Παρακαλώ προσπαθήστε ξανά μετά από λίγο. Αν το σφάλμα παραμένει, παρακαλούμε επικοινωνήστε με την ομάδα υποστήριξης μας.'; @override - String get error => 'Error'; + String get error => 'Σφάλμα'; @override - String get ok => 'Ok'; + String get ok => 'Οκ'; @override - String get faq => 'FAQ'; + String get faq => 'Συχνές Ερωτήσεις'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Επικοινωνήστε με την υποστήριξη'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Στείλτε με email τα αρχεία καταγραφής σας'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Παρακαλώ στείλτε τα αρχεία καταγραφής σας στο \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Αντιγραφή διεύθυνσης email'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Εξαγωγή αρχείων καταγραφής'; @override - String get cancel => 'Cancel'; + String get cancel => 'Ακύρωση'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsEl extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Αναφορά Σφάλματος'; @override String get logsDialogBody => @@ -100,22 +88,22 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Συνδεδεμένο στο $endpoint'; } @override - String get save => 'Save'; + String get save => 'Αποθήκευση'; @override - String get send => 'Send'; + String get send => 'Αποστολή'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή) ή να το στείλετε σε άλλες εφαρμογές;'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή);'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -124,446 +112,456 @@ class StringsLocalizationsEl extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Επαλήθευση'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Μη έγκυρη διεύθυνση email'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Παρακαλώ περιμένετε…'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Επαλήθευση κωδικού πρόσβασης'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Λάθος κωδικός πρόσβασης'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Παρακαλώ δοκιμάστε ξανά'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Εισάγετε κωδικό πρόσβασης'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Εισάγετε τον κωδικό πρόσβασης σας'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Ενεργές συνεδρίες'; @override - String get oops => 'Oops'; + String get oops => 'Ουπς'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Κάτι πήγε στραβά, παρακαλώ προσπαθήστε ξανά'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Αυτό θα σας αποσυνδέσει από αυτή τη συσκευή!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Αυτό θα σας αποσυνδέσει από την ακόλουθη συσκευή:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Τερματισμός συνεδρίας;'; @override - String get terminate => 'Terminate'; + String get terminate => 'Τερματισμός'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Αυτή η συσκευή'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Δημιουργία λογαριασμού'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Αδύναμος'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Μέτριος'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Δυνατός'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Διαγραφή λογαριασμού'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Λυπόμαστε που σας βλέπουμε να φεύγετε. Αντιμετωπίζετε κάποιο πρόβλημα;'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ναι, αποστολή σχολίων'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Όχι, διαγραφή λογαριασμού'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να ξεκινήσετε τη διαγραφή λογαριασμού'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Επιβεβαίωση διαγραφής λογαριασμού'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Αυτός ο λογαριασμός είναι συνδεδεμένος με άλλες εφαρμογές Ente, εάν χρησιμοποιείτε κάποια.\n\nΤα δεδομένα σας, σε όλες τις εφαρμογές Ente, θα προγραμματιστούν για διαγραφή και ο λογαριασμός σας θα διαγραφεί οριστικά.'; @override - String get delete => 'Delete'; + String get delete => 'Διαγραφή'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Δημιουργία νέου λογαριασμού'; @override - String get password => 'Password'; + String get password => 'Κωδικόs πρόσβασης'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Επιβεβαίωση κωδικού πρόσβασης'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Ισχύς κωδικού πρόσβασης: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Πώς ακούσατε για το Ente; (προαιρετικό)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Δεν παρακολουθούμε εγκαταστάσεις εφαρμογών. Θα βοηθούσε αν μας είπατε πού μας βρήκατε!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Όροι'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Πολιτική Απορρήτου'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Καταλαβαίνω ότι αν χάσω τον κωδικό μου μπορεί να χάσω τα δεδομένα μου αφού τα δεδομένα μου είναι από άκρο-σε-άκρο κρυπτογραφημένα.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kρυπτογράφηση'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Σύνδεση'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Καλωσορίσατε και πάλι!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Κάνοντας κλικ στη σύνδεση, συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Χωρίς σύνδεση στο διαδίκτυο'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Παρακαλούμε ελέγξτε τη σύνδεσή σας στο διαδίκτυο και προσπαθήστε ξανά.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Η επαλήθευση απέτυχε, παρακαλώ προσπαθήστε ξανά'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Επαναδημιουργία κωδικού πρόσβασης'; @override String get recreatePasswordBody => - '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).'; + 'Η τρέχουσα συσκευή δεν είναι αρκετά ισχυρή για να επαληθεύσει τον κωδικό πρόσβασής σας, αλλά μπορούμε να τον αναδημιουργήσουμε με έναν τρόπο που λειτουργεί με όλες τις συσκευές.\n\nΠαρακαλούμε συνδεθείτε χρησιμοποιώντας το κλειδί ανάκτησης και αναδημιουργήστε τον κωδικό πρόσβασής σας (μπορείτε να χρησιμοποιήσετε ξανά τον ίδιο αν το επιθυμείτε).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Χρήση κλειδιού ανάκτησης'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Ξέχασα τον κωδικό πρόσβασης σας'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Αλλαγή email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Επαλήθευση email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Έχουμε στείλει ένα μήνυμα στο $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Για να επαναφέρετε τον κωδικό πρόσβασής σας, επαληθεύστε πρώτα το email σας.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Παρακαλώ ελέγξτε τα εισερχόμενά σας (και τα ανεπιθύμητα) για να ολοκληρώσετε την επαλήθευση'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Πατήστε για να εισάγετε τον κωδικό'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Αποστολή email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Επανάληψη αποστολής email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'Η επαλήθευση εξακολουθεί να εκκρεμεί'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Η συνεδρία έληξε'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Η συνεδρία σας έληξε. Παρακαλώ συνδεθείτε ξανά.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Επιβεβαίωση κλειδιού πρόσβασης'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Αναμονή για επαλήθευση...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Προσπαθήστε ξανά'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Έλεγχος κατάστασης'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Είσοδος με TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Ανάκτηση λογαριασμού'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ορισμός κωδικού πρόσβασης'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Αλλαγή κωδικού πρόσβασής'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Επαναφορά κωδικού πρόσβασης'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Κλειδιά κρυπτογράφησης'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Εισάγετε έναν κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για την κρυπτογράφηση των δεδομένων σας'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Εισάγετε ένα νέο κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για να κρυπτογραφήσουμε τα δεδομένα σας'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Δεν αποθηκεύουμε αυτόν τον κωδικό πρόσβασης, οπότε αν τον ξεχάσετε δεν μπορούμε να αποκρυπτογραφήσουμε τα δεδομένα σας'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Πώς λειτουργεί'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Δημιουργία κλειδιών κρυπτογράφησης...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => + 'Ο κωδικός πρόσβασης άλλαξε επιτυχώς'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Αποσύνδεση από άλλες συσκευές'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Αν νομίζετε ότι κάποιος μπορεί να γνωρίζει τον κωδικό πρόσβασής σας, μπορείτε να αναγκάσετε όλες τις άλλες συσκευές που χρησιμοποιούν το λογαριασμό σας να αποσυνδεθούν.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Αποσύνδεση άλλων συσκευών'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Μην αποσυνδεθείτε'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Δημιουργία κλειδιών κρυπτογράφησης…'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Συνέχεια'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Μη ασφαλής συσκευή'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Συγγνώμη, δεν μπορέσαμε να δημιουργήσουμε ασφαλή κλειδιά σε αυτήν τη συσκευή.\n\nπαρακαλώ εγγραφείτε από μια διαφορετική συσκευή.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Το κλειδί ανάκτησης αντιγράφηκε στο πρόχειρο'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Κλειδί ανάκτησης'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Εάν ξεχάσετε τον κωδικό πρόσβασής σας, ο μόνος τρόπος για να ανακτήσετε τα δεδομένα σας είναι με αυτό το κλειδί.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Δεν αποθηκεύουμε αυτό το κλειδί, παρακαλώ αποθηκεύστε αυτό το κλειδί 24 λέξεων σε μια ασφαλή τοποθεσία.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Κάντε το αργότερα'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Αποθήκευση κλειδιού'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Το κλειδί ανάκτησης αποθηκεύτηκε στο φάκελο Λήψεις!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Χωρίς κλειδί ανάκτησης;'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Αυθεντικοποίηση δύο παραγόντων'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Εισάγετε τον 6ψήφιο κωδικό από \nτην εφαρμογή αυθεντικοποίησης'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Χαμένη συσκευή;'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Εισάγετε το κλειδί ανάκτησης σας'; @override - String get recover => 'Recover'; + String get recover => 'Ανάκτηση'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Αποσύνδεση…'; @override - String get immediately => 'Immediately'; + String get immediately => 'Άμεσα'; @override - String get appLock => 'App lock'; + String get appLock => 'Κλείδωμα εφαρμογής'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Αυτόματο κλείδωμα'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Δεν βρέθηκε κλείδωμα συστήματος'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Για να ενεργοποιήσετε το κλείδωμα της συσκευής, παρακαλώ ρυθμίστε τον κωδικό πρόσβασης της συσκευής ή το κλείδωμα οθόνης στις ρυθμίσεις του συστήματός σας.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Επιλέξτε ανάμεσα στην προεπιλεγμένη οθόνη κλειδώματος της συσκευής σας και σε μια προσαρμοσμένη οθόνη κλειδώματος με ένα PIN ή έναν κωδικό πρόσβασης.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Κλείδωμα συσκευής'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Κλείδωμα καρφιτσωμάτων'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Χρόνος μετά τον οποίο η εφαρμογή κλειδώνει μετά την τοποθέτηση στο παρασκήνιο'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Απόκρυψη περιεχομένου'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Απόκρυψη περιεχομένου εφαρμογής στην εναλλαγή εφαρμογών και απενεργοποίηση στιγμιότυπων οθόνης'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Απόκρυψη περιεχομένου εφαρμογών στην εναλλαγή εφαρμογών'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Πάρα πολλές εσφαλμένες προσπάθειες'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Πατήστε για ξεκλείδωμα'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ναι, αποσύνδεση'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε τα μυστικά σας'; @override - String get next => 'Next'; + String get next => 'Επόμενο'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Ορίστε νέο κωδικό πρόσβασης'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Εισαγωγή PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Ορίστε νέο PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Επιβεβαίωση'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Πληκτρολογήστε ξανά τον κωδικό πρόσβασης'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Πληκτρολογήστε ξανά το PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Επαλήθευση ταυτότητας'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Δεν αναγνωρίζεται. Δοκιμάστε ξανά.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Επιτυχία'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Ακύρωση'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Απαιτείται έλεγχος ταυτότητας'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Απαιτούνται βιομετρικά'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Απαιτούνται στοιχεία συσκευής'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Απαιτούνται στοιχεία συσκευής'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Μετάβαση στις ρυθμίσεις'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Η βιομετρική πιστοποίηση δεν έχει ρυθμιστεί στη συσκευή σας. Μεταβείτε στις \'Ρυθμίσεις > Ασφάλεια\' για να προσθέσετε βιομετρική ταυτοποίηση.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Η βιομετρική ταυτοποίηση είναι απενεργοποιημένη. Παρακαλώ κλειδώστε και ξεκλειδώστε την οθόνη σας για να την ενεργοποιήσετε.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'ΟΚ'; @override String get emailAlreadyRegistered => 'Email already registered.'; @@ -572,33 +570,33 @@ class StringsLocalizationsEl extends StringsLocalizations { String get emailNotRegistered => 'Email not registered.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Αυτό το email είναι ήδη σε χρήση'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Το email άλλαξε σε $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Αποτυχία ελέγχου ταυτότητας, παρακαλώ προσπαθήστε ξανά'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Επιτυχής έλεγχος ταυτότητας!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Η συνεδρία έληξε'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Εσφαλμένο κλειδί ανάκτησης'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Το κλειδί ανάκτησης που εισάγατε είναι εσφαλμένο'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Η αυθεντικοποίηση δύο παραγόντων επαναφέρθηκε επιτυχώς'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +609,29 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Ο κωδικός επαλήθευσης σας έχει λήξει'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Εσφαλμένος κωδικός'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Λυπούμαστε, ο κωδικός που εισαγάγατε είναι εσφαλμένος'; + + @override + String get developerSettings => 'Ρυθμίσεις προγραμματιστή'; + + @override + String get serverEndpoint => 'Τερματικό σημείο διακομιστή'; + + @override + String get invalidEndpoint => 'Μη έγκυρο τερματικό σημείο'; + + @override + String get invalidEndpointMessage => + 'Λυπούμαστε, το τερματικό σημείο που εισάγατε δεν είναι έγκυρο. Παρακαλώ εισάγετε ένα έγκυρο τερματικό σημείο και προσπαθήστε ξανά.'; + + @override + String get endpointUpdatedMessage => + 'Το τερματκό σημείο ενημερώθηκε επιτυχώς'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 2a1f3e5b95..37baaf6702 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -76,18 +76,6 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get notAvailable => 'N/A'; - @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - @override String get reportABug => 'Report a bug'; @@ -619,4 +607,20 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get sorryTheCodeYouveEnteredIsIncorrect => 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 7c20ebc7bb..d93fcd07e8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -10,7 +10,7 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get networkHostLookUpErr => - 'No se puede conectar a Ente, por favor verifica tu configuración de red y ponte en contacto con el soporte si el error persiste.'; + 'No se puede conectar a Ente. Por favor, comprueba tu configuración de red y ponte en contacto con el soporte técnico si el error persiste.'; @override String get networkConnectionRefusedErr => @@ -33,21 +33,21 @@ class StringsLocalizationsEs extends StringsLocalizations { String get contactSupport => 'Ponerse en contacto con el equipo de soporte'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Envíe sus registros por correo electrónico'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Por favor, envíe los registros a $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Copiar dirección de correo electrónico'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exportar registros'; @override - String get cancel => 'Cancel'; + String get cancel => 'Cancelar'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsEs extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Reportar un error'; @override String get logsDialogBody => @@ -100,505 +88,514 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Conectado a $endpoint'; } @override - String get save => 'Save'; + String get save => 'Guardar'; @override - String get send => 'Send'; + String get send => 'Enviar'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + '¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto) o enviarlo a otras aplicaciones?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + '¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Correo electrónico'; @override - String get verify => 'Verify'; + String get verify => 'Verificar'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Dirección de correo electrónico no válida'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Por favor, introduce una dirección de correo electrónico válida.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Por favor, espere...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verificar contraseña'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Contraseña incorrecta'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Por favor, inténtalo de nuevo'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Introduzca la contraseña'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Introduce tu contraseña'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sesiones activas'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Algo ha ido mal, por favor, inténtelo de nuevo'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + '¡Esto cerrará la sesión de este dispositivo!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Esto cerrará la sesión del siguiente dispositivo:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => '¿Terminar sesión?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Terminar'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Este dispositivo'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Crear cuenta'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Poco segura'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Moderada'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Segura'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Eliminar cuenta'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Lamentamos que te vayas. ¿Estás teniendo algún problema?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Sí, enviar comentarios'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'No, eliminar cuenta'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Por favor, autentícate para iniciar la eliminación de la cuenta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmar eliminación de la cuenta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Esta cuenta está vinculada a otras aplicaciones de Ente, si utilizas alguna. \n\nSe programará la eliminación de los datos cargados en todas las aplicaciones de Ente, y tu cuenta se eliminará permanentemente.'; @override - String get delete => 'Delete'; + String get delete => 'Borrar'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Crear cuenta nueva'; @override - String get password => 'Password'; + String get password => 'Contraseña'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmar contraseña'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Fortaleza de la contraseña: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => '¿Cómo conoció Ente? (opcional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'No rastreamos la instalación de las aplicaciones. ¡Nos ayudaría si nos dijera dónde nos encontró!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Estoy de acuerdo con los términos del servicio y la política de privacidad'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Términos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Política de Privacidad'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Entiendo que si pierdo mi contraseña podría perder mis datos, ya que mis datos están cifrados de extremo a extremo.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Cifrado'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Iniciar sesión'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '¡Te damos la bienvenida otra vez!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Al hacer clic en iniciar sesión, acepto los términos de servicio y la política de privacidad'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'No hay conexión a Internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Compruebe su conexión a Internet e inténtelo de nuevo.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verificación fallida, por favor inténtalo de nuevo'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Recrear contraseña'; @override String get recreatePasswordBody => - '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).'; + 'El dispositivo actual no es lo suficientemente potente para verificar su contraseña, pero podemos regenerarla de manera que funcione con todos los dispositivos.\n\nPor favor inicie sesión usando su clave de recuperación y regenere su contraseña (puede volver a utilizar la misma si lo desea).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Usar clave de recuperación'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Olvidé mi contraseña'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Cambiar correo electrónico'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verificar correo electrónico'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Hemos enviado un correo electrónico a $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Para restablecer tu contraseña, por favor verifica tu correo electrónico primero.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Por favor revisa tu bandeja de entrada (y spam) para completar la verificación'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Toca para introducir el código'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Enviar correo electrónico'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Reenviar correo electrónico'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'La verificación todavía está pendiente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'La sesión ha expirado'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Tu sesión ha expirado. Por favor, vuelve a iniciar sesión.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verificación de clave de acceso'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Esperando verificación...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Inténtelo de nuevo'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Comprobar estado'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Inicio de sesión con TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Recuperar cuenta'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Establecer contraseña'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Cambiar contraseña'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Restablecer contraseña'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Claves de cifrado'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Introduzca una contraseña que podamos usar para cifrar sus datos'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Introduzca una contraseña nueva que podamos usar para cifrar sus datos'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'No almacenamos esta contraseña, así que si la olvidas, no podremos descifrar tus datos'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cómo funciona'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generando claves de cifrado...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Contraseña cambiada correctamente'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Cerrar sesión en otros dispositivos'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Si crees que alguien puede conocer tu contraseña, puedes forzar a todos los demás dispositivos que usen tu cuenta a cerrar la sesión.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Cerrar la sesión en otros dispositivos'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'No cerrar la sesión'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generando claves de cifrado...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuar'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Dispositivo inseguro'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Lo sentimos, no hemos podido generar claves seguras en este dispositivo.\n\nRegístrate desde un dispositivo diferente.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Clave de recuperación copiada al portapapeles'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Clave de recuperación'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Si olvidas tu contraseña, la única forma de recuperar tus datos es con esta clave.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nosotros no almacenamos esta clave, por favor guarda esta clave de 24 palabras en un lugar seguro.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Hacer esto más tarde'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Guardar clave'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + '¡Clave de recuperación guardada en la carpeta Descargas!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '¿No tienes la clave de recuperación?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autenticación de dos factores'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Ingrese el código de seis dígitos de su aplicación de autenticación'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '¿Dispositivo perdido?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Introduce tu clave de recuperación'; @override - String get recover => 'Recover'; + String get recover => 'Recuperar'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Cerrando sesión...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Inmediatamente'; @override - String get appLock => 'App lock'; + String get appLock => 'Bloqueo de aplicación'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Bloqueo automático'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Bloqueo del sistema no encontrado'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Para activar el bloqueo de la aplicación, por favor configura el código de acceso del dispositivo o el bloqueo de pantalla en los ajustes de tu sistema.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Elija entre la pantalla de bloqueo por defecto de su dispositivo y una pantalla de bloqueo personalizada con un PIN o contraseña.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Bloqueo del dispositivo'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Bloqueo con PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Tiempo tras el cual la aplicación se bloquea después de ser colocada en segundo plano'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Ocultar contenido'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Oculta el contenido de la aplicación en el selector de aplicaciones y desactiva las capturas de pantalla'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Ocultar el contenido de la aplicación en el selector de aplicaciones'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Demasiados intentos incorrectos'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Toca para desbloquear'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + '¿Seguro que quieres cerrar la sesión?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Sí, cerrar la sesión'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Por favor, autentícate para ver tus secretos'; @override - String get next => 'Next'; + String get next => 'Siguiente'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Establece una nueva contraseña'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Ingresa el PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Establecer nuevo PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Confirmar'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Reescribe tu contraseña'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Reescribe tu PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Verificar identidad'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'No reconocido. Inténtalo de nuevo.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Autenticación exitosa'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Cancelar'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Se necesita autenticación biométrica'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => + 'Se necesita autenticación biométrica'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Se necesitan credenciales de dispositivo'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Se necesitan credenciales de dispositivo'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Ir a Ajustes'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'La autenticación biométrica no está configurada en tu dispositivo. Ve a \'Ajustes > Seguridad\' para configurar la autenticación biométrica.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'La autenticación biométrica está deshabilitada. Por favor bloquea y desbloquea la pantalla para habilitarla.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Aceptar'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Correo electrónico ya registrado.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Correo electrónico no registrado.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => + 'Este correo electrónico ya está en uso'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Correo electrónico cambiado a $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Error de autenticación, por favor inténtalo de nuevo'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => '¡Autenticación exitosa!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'La sesión ha expirado'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Clave de recuperación incorrecta'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'La clave de recuperación introducida es incorrecta'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Autenticación de doble factor restablecida con éxito'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +608,28 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Tu código de verificación ha expirado'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Código incorrecto'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Lo sentimos, el código que has introducido es incorrecto'; + + @override + String get developerSettings => 'Ajustes de desarrollador'; + + @override + String get serverEndpoint => 'Endpoint del servidor'; + + @override + String get invalidEndpoint => 'Endpoint no válido'; + + @override + String get invalidEndpointMessage => + 'Lo sentimos, el endpoint introducido no es válido. Por favor, introduce un endpoint válido y vuelve a intentarlo.'; + + @override + String get endpointUpdatedMessage => 'Endpoint actualizado con éxito'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_et.dart b/mobile/packages/strings/lib/l10n/strings_localizations_et.dart new file mode 100644 index 0000000000..49506d60ef --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_et.dart @@ -0,0 +1,629 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Estonian (`et`). +class StringsLocalizationsEt extends StringsLocalizations { + StringsLocalizationsEt([String locale = 'et']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Viga'; + + @override + String get ok => 'Sobib'; + + @override + String get faq => 'KKK'; + + @override + String get contactSupport => 'Võtke ühendust klienditoega'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Katkesta'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Teata veast'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Salvesta'; + + @override + String get send => 'Saada'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Sisesta oma uus e-posti aadress'; + + @override + String get email => 'E-post'; + + @override + String get verify => 'Kinnita'; + + @override + String get invalidEmailTitle => 'Vigane e-posti aadress'; + + @override + String get invalidEmailMessage => 'Palun sisesta korrektne e-posti aadress.'; + + @override + String get pleaseWait => 'Palun oota...'; + + @override + String get verifyPassword => 'Korda salasõna'; + + @override + String get incorrectPasswordTitle => 'Vale salasõna'; + + @override + String get pleaseTryAgain => 'Palun proovi uuesti'; + + @override + String get enterPassword => 'Sisesta salasõna'; + + @override + String get enterYourPasswordHint => 'Sisesta oma salasõna'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Vaat kus lops!'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Nõrk'; + + @override + String get moderateStrength => 'Keskmine'; + + @override + String get strongStrength => 'Tugev'; + + @override + String get deleteAccount => 'Kustuta kasutajakonto'; + + @override + String get deleteAccountQuery => + 'Meil on kahju, et soovid lahkuda. Kas sul tekkis mõni viga või probleem?'; + + @override + String get yesSendFeedbackAction => 'Jah, saadan tagasisidet'; + + @override + String get noDeleteAccountAction => 'Ei, kustuta kasutajakonto'; + + @override + String get initiateAccountDeleteTitle => + 'Kasutajakonto kustutamiseks palun tuvasta end'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Kustuta'; + + @override + String get createNewAccount => 'Loo uus kasutajakonto'; + + @override + String get password => 'Salasõna'; + + @override + String get confirmPassword => 'Korda salasõna'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Salasõna tugevus: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Kasutustingimused'; + + @override + String get privacyPolicyTitle => 'Privaatsusreeglid'; + + @override + String get ackPasswordLostWarning => + 'Ma saan aru, et salasõna kaotamisel kaotan ka ligipääsu oma andmetele - minu andmed on ju läbivalt krüptitud.'; + + @override + String get encryption => 'Krüptimine'; + + @override + String get logInLabel => 'Logi sisse'; + + @override + String get welcomeBack => 'Tere tulemast tagasi!'; + + @override + String get loginTerms => + 'Sisselogdes nõustun kasutustingimustega ja privaatsusreeglitega'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Loo salasõna uuesti'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Kasuta taastevõtit'; + + @override + String get forgotPassword => 'Unustasin salasõna'; + + @override + String get changeEmail => 'Muuda e-posti aadressi'; + + @override + String get verifyEmail => 'Kinnita e-posti aadress'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Saada e-kiri'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Proovi uuesti'; + + @override + String get checkStatus => 'Kontrolli olekut'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Taasta oma kasutajakonto'; + + @override + String get setPasswordTitle => 'Sisesta salasõna'; + + @override + String get changePasswordTitle => 'Muuda salasõna'; + + @override + String get resetPasswordTitle => 'Lähtesta salasõna'; + + @override + String get encryptionKeys => 'Krüptovõtmed'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'Kuidas see töötab'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Salasõna muutmine õnnestus'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Loon krüptovõtmeid...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Taastevõti on kopeeritud lõikelauale'; + + @override + String get recoveryKey => 'Taastevõti'; + + @override + String get recoveryKeyOnForgotPassword => + 'Kui unustad oma salasõna, siis see krüptovõti on ainus võimalus sinu andmete taastamiseks.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Salvesta võti'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'Sul pole taastevõtit?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Sisesta oma autentimisrakendusest\n6-numbriline kood'; + + @override + String get lostDeviceTitle => 'Kas kaotasid oma seadme?'; + + @override + String get enterRecoveryKeyHint => 'Sisesta oma taastevõti'; + + @override + String get recover => 'Taasta'; + + @override + String get loggingOut => 'Väljalogimine...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => + 'Kas oled kindel, et soovid välja logida?'; + + @override + String get yesLogout => 'Jah, logi välja'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Sisesta uus salasõna'; + + @override + String get enterPin => 'Sisesta PIN-kood'; + + @override + String get setNewPin => 'Määra uus PIN-kood'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Sisesta salasõna uuesti'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => + 'E-posti aadress on juba registreeritud.'; + + @override + String get emailNotRegistered => 'E-posti aadress pole registreeritud.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Sessioon on aegunud'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fa.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fa.dart new file mode 100644 index 0000000000..8545b269d0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fa.dart @@ -0,0 +1,626 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Persian (`fa`). +class StringsLocalizationsFa extends StringsLocalizations { + StringsLocalizationsFa([String locale = 'fa']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'به نظر می‌رسد مشکلی پیش آمده است. لطفا بعد از مدتی دوباره تلاش کنید. اگر همچنان با خطا مواجه می‌شوید، لطفا با تیم پشتیبانی ما ارتباط برقرار کنید.'; + + @override + String get error => 'خطا'; + + @override + String get ok => 'تایید'; + + @override + String get faq => 'سوالات متداول'; + + @override + String get contactSupport => 'ارتباط با پشتیبانی'; + + @override + String get emailYourLogs => 'لاگ‌های خود را ایمیل کنید'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'لطفا لاگ‌ها را به ایمیل زیر ارسال کنید \n$toEmail'; + } + + @override + String get copyEmailAddress => 'کپی آدرس ایمیل'; + + @override + String get exportLogs => 'صدور لاگ‌ها'; + + @override + String get cancel => 'لغو'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'گزارش یک اشکال'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'متصل شده به $endpoint'; + } + + @override + String get save => 'ذخیره'; + + @override + String get send => 'ارسال'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'ایمیل'; + + @override + String get verify => 'تایید'; + + @override + String get invalidEmailTitle => 'آدرس ایمیل نامعتبر است'; + + @override + String get invalidEmailMessage => 'لطفا یک آدرس ایمیل معتبر وارد کنید.'; + + @override + String get pleaseWait => 'لطفا صبر کنید...'; + + @override + String get verifyPassword => 'تایید رمز عبور'; + + @override + String get incorrectPasswordTitle => 'رمز عبور نادرست'; + + @override + String get pleaseTryAgain => 'لطفا دوباره تلاش کنید'; + + @override + String get enterPassword => 'رمز عبور را وارد کنید'; + + @override + String get enterYourPasswordHint => 'رمز عبور خود را وارد کنید'; + + @override + String get activeSessions => 'نشست های فعال'; + + @override + String get oops => 'اوه'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'مشکلی پیش آمده، لطفا دوباره تلاش کنید'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'این کار شما را از این دستگاه خارج می‌کند!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'با این کار شما از دستگاه زیر خارج می‌شوید:'; + + @override + String get terminateSession => 'خروچ دستگاه؟'; + + @override + String get terminate => 'خروج'; + + @override + String get thisDevice => 'این دستگاه'; + + @override + String get createAccount => 'ایجاد حساب کاربری'; + + @override + String get weakStrength => 'ضعیف'; + + @override + String get moderateStrength => 'متوسط'; + + @override + String get strongStrength => 'قوی'; + + @override + String get deleteAccount => 'حذف حساب کاربری'; + + @override + String get deleteAccountQuery => + 'از رفتن شما متاسفیم. آیا با مشکلی روبرو هستید؟'; + + @override + String get yesSendFeedbackAction => 'بله، ارسال بازخورد'; + + @override + String get noDeleteAccountAction => 'خیر، حساب کاربری را حذف کن'; + + @override + String get initiateAccountDeleteTitle => + 'لطفا جهت شروع فرآیند حذف حساب کاربری، اعتبارسنجی کنید'; + + @override + String get confirmAccountDeleteTitle => 'تایید حذف حساب کاربری'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'حذف'; + + @override + String get createNewAccount => 'ایجاد حساب کاربری جدید'; + + @override + String get password => 'رمز عبور'; + + @override + String get confirmPassword => 'تایید رمز عبور'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'قدرت رمز عبور: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'از کجا در مورد Ente شنیدی؟ (اختیاری)'; + + @override + String get hearUsExplanation => + 'ما نصب برنامه را ردیابی نمی‌کنیم. اگر بگویید کجا ما را پیدا کردید، به ما کمک می‌کند!'; + + @override + String get signUpTerms => + 'با شرایط استفاده از خدمات و سیاست حفظ حریم خصوصی موافقت می‌کنم'; + + @override + String get termsOfServicesTitle => 'شرایط و ضوابط'; + + @override + String get privacyPolicyTitle => 'سیاست حفظ حریم خصوصی'; + + @override + String get ackPasswordLostWarning => + 'می‌دانم که اگر رمز عبور خود را گم کنم، از آنجایی که اطلاعات من رمزگذاری سرتاسری شده است، ممکن است اطلاعاتم را از دست بدهم.'; + + @override + String get encryption => 'رمزگذاری'; + + @override + String get logInLabel => 'ورود'; + + @override + String get welcomeBack => 'خوش آمدید!'; + + @override + String get loginTerms => + 'با کلیک روی ورود، با شرایط استفاده از خدمات و سیاست حفظ حریم خصوصی موافقت می‌کنم'; + + @override + String get noInternetConnection => 'نبود اتصال اینترنت'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'لطفا اتصال اینترنت خود را بررسی کنید و دوباره امتحان کنید.'; + + @override + String get verificationFailedPleaseTryAgain => + 'تایید ناموفق بود، لطفا مجددا تلاش کنید'; + + @override + String get recreatePasswordTitle => 'بازتولید رمز عبور'; + + @override + String get recreatePasswordBody => + 'دستگاه فعلی جهت تایید رمز عبور شما به اندازه کافی قدرتمند نیست، اما ما میتوانیم آن را به گونه ای بازتولید کنیم که با همه دستگاه‌ها کار کند.\n\nلطفا با استفاده از کلید بازیابی خود وارد شوید و رمز عبور خود را دوباره ایجاد کنید (در صورت تمایل می‌توانید دوباره از همان رمز عبور استفاده کنید).'; + + @override + String get useRecoveryKey => 'از کلید بازیابی استفاده کنید'; + + @override + String get forgotPassword => 'فراموشی رمز عبور'; + + @override + String get changeEmail => 'تغییر ایمیل'; + + @override + String get verifyEmail => 'تایید ایمیل'; + + @override + String weHaveSendEmailTo(String email) { + return 'ما یک ایمیل به $email ارسال کرده‌ایم'; + } + + @override + String get toResetVerifyEmail => + 'برای تنظیم مجدد رمز عبور، لطفا ابتدا ایمیل خود را تایید کنید.'; + + @override + String get checkInboxAndSpamFolder => + 'لطفا صندوق ورودی (و هرزنامه) خود را برای تایید کامل بررسی کنید'; + + @override + String get tapToEnterCode => 'برای وارد کردن کد ضربه بزنید'; + + @override + String get sendEmail => 'ارسال ایمیل'; + + @override + String get resendEmail => 'ارسال مجدد ایمیل'; + + @override + String get passKeyPendingVerification => 'تأییدیه هنوز در انتظار است'; + + @override + String get loginSessionExpired => 'نشست منقضی شده است'; + + @override + String get loginSessionExpiredDetails => + 'نشست شما منقضی شده. لطفا دوباره وارد شوید.'; + + @override + String get passkeyAuthTitle => 'تایید کردن پسکی'; + + @override + String get waitingForVerification => 'درانتظار تأییدیه...'; + + @override + String get tryAgain => 'دوباره امتحان کنید'; + + @override + String get checkStatus => 'بررسی وضعیت'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'بازیابی حساب کاربری'; + + @override + String get setPasswordTitle => 'تنظیم پسورد'; + + @override + String get changePasswordTitle => 'تغییر رمز عبور'; + + @override + String get resetPasswordTitle => 'بازنشانی رمز عبور'; + + @override + String get encryptionKeys => 'کلیدهای رمزنگاری'; + + @override + String get enterPasswordToEncrypt => + 'رمز عبوری را وارد کنید که بتوانیم از آن برای رمزگذاری اطلاعات شما استفاده کنیم'; + + @override + String get enterNewPasswordToEncrypt => + 'رمز عبور جدیدی را وارد کنید که بتوانیم از آن برای رمزگذاری اطلاعات شما استفاده کنیم'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'چگونه کار می‌کند'; + + @override + String get generatingEncryptionKeys => 'در حال تولید کلیدهای رمزگذاری...'; + + @override + String get passwordChangedSuccessfully => 'رمز عبور با موفقیت تغییر کرد'; + + @override + String get signOutFromOtherDevices => 'از دستگاه های دیگر خارج شوید'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'از دستگاه های دیگر خارج شوید'; + + @override + String get doNotSignOut => 'خارج نشوید'; + + @override + String get generatingEncryptionKeysTitle => + 'در حال تولید کلید‌های رمزگذاری...'; + + @override + String get continueLabel => 'ادامه'; + + @override + String get insecureDevice => 'دستگاه ناامن'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'با عرض پوزش، ما نمی‌توانیم کلیدهای امن را در این دستگاه تولید کنیم.\n\nلطفا از دستگاه دیگری ثبت نام کنید.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'کلید بازیابی به حافظه موقت کپی شد'; + + @override + String get recoveryKey => 'کلید بازیابی'; + + @override + String get recoveryKeyOnForgotPassword => + 'اگر رمز عبور خود را فراموش کرده‌اید، این کد تنها راهی است که با آن می‌توانید اطلاعات خود را بازیابی کنید.'; + + @override + String get recoveryKeySaveDescription => + 'ما این کلید را ذخیره نمی‌کنیم، لطفا این کلید ۲۴ کلمه‌ای را در مکانی امن ذخیره کنید.'; + + @override + String get doThisLater => 'بعداً انجام شود'; + + @override + String get saveKey => 'ذخیره کلید'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'کلید بازیابی ندارید؟'; + + @override + String get twoFactorAuthTitle => 'احراز هویت دو مرحله‌ای'; + + @override + String get enterCodeHint => + 'کد تایید ۶ رقمی را از برنامه\nاحراز هویت خود وارد کنید'; + + @override + String get lostDeviceTitle => 'دستگاه را گم کرده‌اید؟'; + + @override + String get enterRecoveryKeyHint => 'کلید بازیابی خود را وارد کنید'; + + @override + String get recover => 'بازیابی'; + + @override + String get loggingOut => 'در حال خروج از سیستم...'; + + @override + String get immediately => 'فوری'; + + @override + String get appLock => 'قفل برنامه'; + + @override + String get autoLock => 'قفل خودکار'; + + @override + String get noSystemLockFound => 'هیج قبل سیستمی پیدا نشد'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'قفل دستگاه'; + + @override + String get pinLock => 'پین قفل'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'پنهان کردن محتوا'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'برای باز کردن قفل ضربه بزنید'; + + @override + String get areYouSureYouWantToLogout => 'آیا مطمئنید که می‌خواهید خارج شوید؟'; + + @override + String get yesLogout => 'بله، خروج'; + + @override + String get authToViewSecrets => 'لطفا جهت دیدن راز های خود احراز هویت کنید'; + + @override + String get next => 'بعدی'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'پین را وارد کنید'; + + @override + String get setNewPin => 'پین جدید انتخاب کنید'; + + @override + String get confirm => 'تایید'; + + @override + String get reEnterPassword => 'رمز عبور را مجدداً وارد کنید'; + + @override + String get reEnterPin => 'پین را مجدداً وارد کنید'; + + @override + String get androidBiometricHint => 'تایید هویت'; + + @override + String get androidBiometricNotRecognized => 'شناخته نشد. دوباره امتحان کنید.'; + + @override + String get androidBiometricSuccess => 'موفقیت'; + + @override + String get androidCancelButton => 'لغو'; + + @override + String get androidSignInTitle => 'احراز هویت لازم است'; + + @override + String get androidBiometricRequiredTitle => 'بیومتریک لازم است'; + + @override + String get androidDeviceCredentialsRequiredTitle => 'اعتبار دستگاه لازم است'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'اعتبار دستگاه لازم است'; + + @override + String get goToSettings => 'به تنظیمات بروید'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'تأیید'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'این ایمیل درحال استفاده است'; + + @override + String emailChangedTo(String newEmail) { + return 'ایمیل عوض شد به $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'احراز هویت ناموفق بود، لطفا دوباره تلاش کنید'; + + @override + String get authenticationSuccessful => 'احراز هویت موفق آمیز!'; + + @override + String get sessionExpired => 'نشست منقضی شده است'; + + @override + String get incorrectRecoveryKey => 'کلید بازیابی درست نیست'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'کلید بازیابی که وارد کردید درست نیست'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'احراز هویت دو مرحله با موفقیت بازنشانی شد'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'کد تایید شما باطل شد'; + + @override + String get incorrectCode => 'کد اشتباه'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'معظرت میخوام، کدی که شما وارد کردید اشتباه است'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fi.dart new file mode 100644 index 0000000000..3081b85c8f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fi.dart @@ -0,0 +1,627 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Finnish (`fi`). +class StringsLocalizationsFi extends StringsLocalizations { + StringsLocalizationsFi([String locale = 'fi']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Virhe'; + + @override + String get ok => 'Selvä'; + + @override + String get faq => 'Usein kysyttyä'; + + @override + String get contactSupport => 'Ota yhteyttä käyttötukeen'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Keskeytä'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Ilmoita virhetoiminnosta'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Tallenna'; + + @override + String get send => 'Lähetä'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Sähköposti'; + + @override + String get verify => 'Vahvista'; + + @override + String get invalidEmailTitle => 'Epäkelpo sähköpostiosoite'; + + @override + String get invalidEmailMessage => 'Syötä kelpoisa sähköpostiosoite.'; + + @override + String get pleaseWait => 'Odota hetki...'; + + @override + String get verifyPassword => 'Vahvista salasana'; + + @override + String get incorrectPasswordTitle => 'Salasana on väärin'; + + @override + String get pleaseTryAgain => 'Yritä uudestaan'; + + @override + String get enterPassword => 'Syötä salasana'; + + @override + String get enterYourPasswordHint => 'Syötä salasanasi'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Hupsista'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Luo tili'; + + @override + String get weakStrength => 'Heikko salasana'; + + @override + String get moderateStrength => 'Kohtalainen salasana'; + + @override + String get strongStrength => 'Vahva salasana'; + + @override + String get deleteAccount => 'Poista tili'; + + @override + String get deleteAccountQuery => + 'Olemme pahoillamme että lähdet keskuudestamme. Kohtasitko käytössä jonkin ongelman?'; + + @override + String get yesSendFeedbackAction => 'Kyllä, lähetä palautetta'; + + @override + String get noDeleteAccountAction => 'En, poista tili'; + + @override + String get initiateAccountDeleteTitle => + 'Ole hyvä ja tee todennus käynnistääksesi tilisi poistoprosessin'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Poista'; + + @override + String get createNewAccount => 'Luo uusi tili'; + + @override + String get password => 'Salasana'; + + @override + String get confirmPassword => 'Vahvista salasana'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Salaus'; + + @override + String get logInLabel => 'Kirjaudu sisään'; + + @override + String get welcomeBack => 'Tervetuloa takaisin!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Käytä palautusavainta'; + + @override + String get forgotPassword => 'Olen unohtanut salasanani'; + + @override + String get changeEmail => 'vaihda sähköpostiosoitteesi'; + + @override + String get verifyEmail => 'Vahvista sähköpostiosoite'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Lähetä sähköpostia'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Avainkoodin vahvistus'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Palauta tilisi'; + + @override + String get setPasswordTitle => 'Luo salasana'; + + @override + String get changePasswordTitle => 'Vaihda salasana'; + + @override + String get resetPasswordTitle => 'Nollaa salasana'; + + @override + String get encryptionKeys => 'Salausavaimet'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Luodaan salausavaimia...'; + + @override + String get passwordChangedSuccessfully => 'Salasana vaihdettu onnistuneesti'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Luodaan salausavaimia...'; + + @override + String get continueLabel => 'Jatka'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Palautusavain kopioitu leikepöydälle'; + + @override + String get recoveryKey => 'Palautusavain'; + + @override + String get recoveryKeyOnForgotPassword => + 'Jos unohdat salasanasi, ainoa tapa palauttaa tietosi on tällä avaimella.'; + + @override + String get recoveryKeySaveDescription => + 'Emme tallenna tätä avainta, ole hyvä ja tallenna tämä 24 sanan avain turvalliseen paikkaan.'; + + @override + String get doThisLater => 'Tee tämä myöhemmin'; + + @override + String get saveKey => 'Tallenna avain'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'Ei palautusavainta?'; + + @override + String get twoFactorAuthTitle => 'Kaksivaiheinen vahvistus'; + + @override + String get enterCodeHint => + 'Syötä 6-merkkinen koodi varmennussovelluksestasi'; + + @override + String get lostDeviceTitle => 'Kadonnut laite?'; + + @override + String get enterRecoveryKeyHint => 'Syötä palautusavaimesi'; + + @override + String get recover => 'Palauta'; + + @override + String get loggingOut => 'Kirjaudutaan ulos...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Kyllä, kirjaudu ulos'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Kirjautuminen onnistui'; + + @override + String get androidCancelButton => 'Peruuta'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Mene asetuksiin'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Istunto on vanheutunut'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 4d248e0f09..3375cff00a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -33,21 +33,21 @@ class StringsLocalizationsFr extends StringsLocalizations { String get contactSupport => 'Contacter le support'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Envoyez vos logs par e-mail'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Envoyez les logs à $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Copier l’adresse e-mail'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exporter les journaux'; @override - String get cancel => 'Cancel'; + String get cancel => 'Annuler'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsFr extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Signaler un bug'; @override String get logsDialogBody => @@ -100,505 +88,516 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Connecté à $endpoint'; } @override - String get save => 'Save'; + String get save => 'Sauvegarder'; @override - String get send => 'Send'; + String get send => 'Envoyer'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ou l\'envoyer à d\'autres applications ?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Saisissez votre nouvelle adresse email'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Vérifier'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Adresse e-mail invalide'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Veuillez saisir une adresse e-mail valide.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Veuillez patienter...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Vérifier le mot de passe'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Mot de passe incorrect'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Veuillez réessayer'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Saisissez le mot de passe'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Entrez votre mot de passe'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sessions actives'; @override - String get oops => 'Oops'; + String get oops => 'Oups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Quelque chose s\'est mal passé, veuillez recommencer'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Cela vous déconnectera de cet appareil !'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Cela vous déconnectera de l\'appareil suivant :'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Quitter la session ?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Quitter'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Cet appareil'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Créer un compte'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Faible'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Modéré'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Fort'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Supprimer le compte'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Nous sommes désolés de vous voir partir. Rencontrez-vous un problème ?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Oui, envoyer un commentaire'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Non, supprimer le compte'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Veuillez vous authentifier pour débuter la suppression du compte'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmer la suppression du compte'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Ce compte est lié à d\'autres applications ente, si vous en utilisez une.\n\nVos données téléchargées, dans toutes les applications ente, seront planifiées pour suppression, et votre compte sera définitivement supprimé.'; @override - String get delete => 'Delete'; + String get delete => 'Supprimer'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Créer un nouveau compte'; @override - String get password => 'Password'; + String get password => 'Mot de passe'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmer le mot de passe'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Force du mot de passe : $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => + 'Comment avez-vous entendu parler de Ente? (facultatif)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nous ne suivons pas les installations d\'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'J\'accepte les conditions d\'utilisation et la politique de confidentialité'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Conditions'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Politique de confidentialité'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Je comprends que si je perds mon mot de passe, je risque de perdre mes données puisque celles-ci sont chiffrées de bout en bout.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Chiffrement'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Connexion'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Bon retour parmi nous !'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'En cliquant sur \"Connexion\", j\'accepte les conditions d\'utilisation et la politique de confidentialité'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Aucune connexion internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Veuillez vérifier votre connexion internet puis réessayer.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'La vérification a échouée, veuillez réessayer'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Recréer le mot de passe'; @override String get recreatePasswordBody => - '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).'; + 'L\'appareil actuel n\'est pas assez puissant pour vérifier votre mot de passe, donc nous avons besoin de le régénérer une fois d\'une manière qu\'il fonctionne avec tous les périphériques.\n\nVeuillez vous connecter en utilisant votre clé de récupération et régénérer votre mot de passe (vous pouvez utiliser le même si vous le souhaitez).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Utiliser la clé de récupération'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Mot de passe oublié'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Modifier l\'e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Vérifier l\'e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Nous avons envoyé un mail à $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Pour réinitialiser votre mot de passe, veuillez d\'abord vérifier votre e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Veuillez consulter votre boîte de courriels (et les spam) pour compléter la vérification'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Appuyez pour entrer un code'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Envoyer un e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Renvoyer le courriel'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'La vérification est toujours en attente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Session expirée'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Votre session a expiré. Veuillez vous reconnecter.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Vérification du code d\'accès'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'En attente de vérification...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Réessayer'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Vérifier le statut'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Se connecter avec un code TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Récupérer un compte'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Définir le mot de passe'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Modifier le mot de passe'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Réinitialiser le mot de passe'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Clés de chiffrement'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Entrez un mot de passe que nous pouvons utiliser pour chiffrer vos données'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Entrez un nouveau mot de passe que nous pouvons utiliser pour chiffrer vos données'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Nous ne stockons pas ce mot de passe. Si vous l\'oubliez, nous ne pourrons pas déchiffrer vos données'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Comment ça fonctionne'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Génération des clés de chiffrement...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => + 'Le mot de passe a été modifié avec succès'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Déconnexion des autres appareils'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Si vous pensez que quelqu\'un connaît peut-être votre mot de passe, vous pouvez forcer tous les autres appareils utilisant votre compte à se déconnecter.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Déconnecter les autres appareils'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Ne pas se déconnecter'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Génération des clés de chiffrement...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuer'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Appareil non sécurisé'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '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.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Clé de récupération copiée dans le presse-papiers'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Clé de récupération'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nous ne stockons pas cette clé, veuillez enregistrer cette clé de 24 mots dans un endroit sûr.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Plus tard'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Enregistrer la clé'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Clé de récupération enregistrée dans le dossier Téléchargements !'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Pas de clé de récupération ?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Authentification à deux facteurs'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Entrez le code à 6 chiffres de votre application d\'authentification'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Appareil perdu ?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Saisissez votre clé de récupération'; @override - String get recover => 'Recover'; + String get recover => 'Restaurer'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Deconnexion...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Immédiatement'; @override - String get appLock => 'App lock'; + String get appLock => 'Verrouillage d\'application'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Verrouillage automatique'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Aucun verrou système trouvé'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + '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.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Choisissez entre l\'écran de verrouillage par défaut de votre appareil et un écran de verrouillage par code PIN ou mot de passe personnalisé.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Verrouillage de l\'appareil'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Verrouillage par code PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Délai après lequel l\'application se verrouille une fois qu\'elle a été mise en arrière-plan'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Masquer le contenu'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Masque le contenu de l\'application sur le menu et désactive les captures d\'écran'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Masque le contenu de l\'application sur le menu'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Trop de tentatives incorrectes'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Appuyer pour déverrouiller'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Êtes-vous sûr de vouloir vous déconnecter ?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Oui, se déconnecter'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Veuillez vous authentifier pour voir vos souvenirs'; @override - String get next => 'Next'; + String get next => 'Suivant'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Définir un nouveau mot de passe'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Saisir le code PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Définir un nouveau code PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Confirmer'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Ressaisir le mot de passe'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Ressaisir le code PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Vérifier l’identité'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Non reconnu. Veuillez réessayer.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Parfait'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Annuler'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Authentification requise'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Empreinte digitale requise'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Identifiants de l\'appareil requis'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Identifiants de l\'appareil requis'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Allez dans les paramètres'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'L\'authentification biométrique n\'est pas configurée sur votre appareil. Allez dans \'Paramètres > Sécurité\' pour l\'ajouter.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'L\'authentification biométrique est désactivée. Veuillez verrouiller et déverrouiller votre écran pour l\'activer.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Ok'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-mail déjà enregistré.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-mail non enregistré.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Cette adresse mail est déjà utilisé'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'L\'e-mail a été changé en $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'L\'authentification a échouée, veuillez réessayer'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Authentification réussie!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Session expirée'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Clé de récupération non valide'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'La clé de récupération que vous avez entrée est incorrecte'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'L\'authentification à deux facteurs a été réinitialisée avec succès '; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +610,29 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Votre code de vérification a expiré'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Code non valide'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Le code que vous avez saisi est incorrect'; + + @override + String get developerSettings => 'Paramètres du développeur'; + + @override + String get serverEndpoint => 'Point de terminaison serveur'; + + @override + String get invalidEndpoint => 'Point de terminaison non valide'; + + @override + String get invalidEndpointMessage => + 'Désolé, le point de terminaison que vous avez entré n\'est pas valide. Veuillez en entrer un valide puis réessayez.'; + + @override + String get endpointUpdatedMessage => + 'Point de terminaison mis à jour avec succès'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_gu.dart b/mobile/packages/strings/lib/l10n/strings_localizations_gu.dart new file mode 100644 index 0000000000..caebb756ec --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_gu.dart @@ -0,0 +1,627 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Gujarati (`gu`). +class StringsLocalizationsGu extends StringsLocalizations { + StringsLocalizationsGu([String locale = 'gu']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'સારું'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'સહાયતા માટે સંપર્ક કરો'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'રદ કરો'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'બગની જાણ કરો'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'ઇમેઇલ'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'કૃપા કરીને રાહ જુવો...'; + + @override + String get verifyPassword => 'પાસવર્ડ ચકાસો'; + + @override + String get incorrectPasswordTitle => 'ખોટો પાસવર્ડ'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'પાસવર્ડ દાખલ કરો'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'કાઢી નાખો'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'ફરી તમારુ સ્વાગત છે!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'પુનઃપ્રાપ્તિ કીનો ઉપયોગ કરો'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'ઈ - મેઈલ બદલો'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => + 'એન્ક્રિપ્શન ચાવીઓ જનરેટ કરી રહ્યાં છીએ...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'પુનઃપ્રાપ્તિ ચાવી'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'લૉગ આઉટ થઈ રહ્યું છે...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'સફળતા'; + + @override + String get androidCancelButton => 'રદ કરો'; + + @override + String get androidSignInTitle => 'પ્રમાણીકરણ જરૂરી છે'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'સેટિંગ માં જાઓ'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'બરાબર'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'સત્ર સમાપ્ત થયુ'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_he.dart b/mobile/packages/strings/lib/l10n/strings_localizations_he.dart new file mode 100644 index 0000000000..c1bc4531fb --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_he.dart @@ -0,0 +1,621 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hebrew (`he`). +class StringsLocalizationsHe extends StringsLocalizations { + StringsLocalizationsHe([String locale = 'he']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'נראה שמשהו לא פעל כשורה. אנא נסה שוב אחרי כמה זמן. אם הבעיה ממשיכה, אנא צור קשר עם צוות התמיכה שלנו.'; + + @override + String get error => 'שגיאה'; + + @override + String get ok => 'אוקיי'; + + @override + String get faq => 'שאלות נפוצות'; + + @override + String get contactSupport => 'צור קשר עם התמיכה'; + + @override + String get emailYourLogs => 'שלח באימייל את הלוגים שלך'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'אנא שלחו את הלוגים האלו ל-$toEmail'; + } + + @override + String get copyEmailAddress => 'העתק כתובת דוא\"ל'; + + @override + String get exportLogs => 'ייצוא לוגים'; + + @override + String get cancel => 'בטל'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'דווח על באג'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'שמור'; + + @override + String get send => 'שלח'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'דוא\"ל'; + + @override + String get verify => 'אמת'; + + @override + String get invalidEmailTitle => 'כתובת דוא״ל לא תקינה'; + + @override + String get invalidEmailMessage => 'אנא הכנס כתובת דוא\"ל תקינה.'; + + @override + String get pleaseWait => 'אנא המתן...'; + + @override + String get verifyPassword => 'אמת סיסמא'; + + @override + String get incorrectPasswordTitle => 'סיסמא לא נכונה'; + + @override + String get pleaseTryAgain => 'אנא נסה שנית'; + + @override + String get enterPassword => 'הזן את הסיסמה'; + + @override + String get enterYourPasswordHint => 'הכנס סיסמא'; + + @override + String get activeSessions => 'חיבורים פעילים'; + + @override + String get oops => 'אופס'; + + @override + String get somethingWentWrongPleaseTryAgain => 'משהו השתבש, אנא נסה שנית'; + + @override + String get thisWillLogYouOutOfThisDevice => 'זה ינתק אותך במכשיר זה!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'זה ינתק אותך מהמכשיר הבא:'; + + @override + String get terminateSession => 'סיים חיבור?'; + + @override + String get terminate => 'סיים'; + + @override + String get thisDevice => 'מכשיר זה'; + + @override + String get createAccount => 'צור חשבון'; + + @override + String get weakStrength => 'חלש'; + + @override + String get moderateStrength => 'מתון'; + + @override + String get strongStrength => 'חזק'; + + @override + String get deleteAccount => 'מחק חשבון'; + + @override + String get deleteAccountQuery => + 'אנו מצטערים שאתה עוזב. האם יש בעיות שאתה חווה?'; + + @override + String get yesSendFeedbackAction => 'כן, שלח משוב'; + + @override + String get noDeleteAccountAction => 'לא, מחק את החשבון'; + + @override + String get initiateAccountDeleteTitle => + 'אנא אמת על מנת להתחיל את מחיקת החשבון שלך'; + + @override + String get confirmAccountDeleteTitle => 'אישור מחיקת חשבון'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'למחוק'; + + @override + String get createNewAccount => 'צור חשבון חדש'; + + @override + String get password => 'סיסמא'; + + @override + String get confirmPassword => 'אמת סיסמא'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'חוזק הסיסמא: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'אני מסכים לתנאי שירות ולמדיניות הפרטיות'; + + @override + String get termsOfServicesTitle => 'תנאים'; + + @override + String get privacyPolicyTitle => 'מדיניות פרטיות'; + + @override + String get ackPasswordLostWarning => + 'אני מבין שאם אאבד את הסיסמא, אני עלול לאבד את המידע שלי מכיוון שהמידע שלי מוצפן מקצה אל קצה.'; + + @override + String get encryption => 'הצפנה'; + + @override + String get logInLabel => 'התחבר'; + + @override + String get welcomeBack => 'ברוך שובך!'; + + @override + String get loginTerms => + 'על ידי לחיצה על התחברות, אני מסכים לתנאי שירות ולמדיניות הפרטיות'; + + @override + String get noInternetConnection => 'אין חיבור לאינטרנט'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'אנא בדוק את חיבור האינטרנט שלך ונסה שוב.'; + + @override + String get verificationFailedPleaseTryAgain => 'אימות נכשל, אנא נסה שנית'; + + @override + String get recreatePasswordTitle => 'צור סיסמא מחדש'; + + @override + String get recreatePasswordBody => + 'המכשיר הנוכחי אינו חזק מספיק כדי לאמת את הסיסמא שלך, אבל אנחנו יכולים ליצור מחדש בצורה שתעבוד עם כל המכשירים.\n\nאנא התחבר בעזרת המפתח שחזור שלך וצור מחדש את הסיסמא שלך (אתה יכול להשתמש באותה אחת אם אתה רוצה).'; + + @override + String get useRecoveryKey => 'השתמש במפתח שחזור'; + + @override + String get forgotPassword => 'שכחתי סיסמה'; + + @override + String get changeEmail => 'שנה דוא\"ל'; + + @override + String get verifyEmail => 'אימות דוא\"ל'; + + @override + String weHaveSendEmailTo(String email) { + return 'שלחנו דוא\"ל ל$email'; + } + + @override + String get toResetVerifyEmail => + 'כדי לאפס את הסיסמא שלך, אנא אמת את האימייל שלך קודם.'; + + @override + String get checkInboxAndSpamFolder => + 'אנא בדוק את תיבת הדואר שלך (והספאם) כדי להשלים את האימות'; + + @override + String get tapToEnterCode => 'הקש כדי להזין את הקוד'; + + @override + String get sendEmail => 'שלח אימייל'; + + @override + String get resendEmail => 'שלח דוא\"ל מחדש'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'נסה שוב'; + + @override + String get checkStatus => 'בדוק סטטוס'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'שחזר חשבון'; + + @override + String get setPasswordTitle => 'הגדר סיסמא'; + + @override + String get changePasswordTitle => 'שנה סיסמה'; + + @override + String get resetPasswordTitle => 'איפוס סיסמה'; + + @override + String get encryptionKeys => 'מפתחות ההצפנה'; + + @override + String get enterPasswordToEncrypt => + 'הזן סיסמא כדי שנוכל להצפין את המידע שלך'; + + @override + String get enterNewPasswordToEncrypt => + 'הכנס סיסמא חדשה כדי שנוכל להצפין את המידע שלך'; + + @override + String get passwordWarning => + 'אנחנו לא שומרים את הסיסמא הזו, לכן אם אתה שוכח אותה, אנחנו לא יכולים לפענח את המידע שלך'; + + @override + String get howItWorks => 'איך זה עובד'; + + @override + String get generatingEncryptionKeys => 'יוצר מפתחות הצפנה...'; + + @override + String get passwordChangedSuccessfully => 'הסיסמה הוחלפה בהצלחה'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'יוצר מפתחות הצפנה...'; + + @override + String get continueLabel => 'המשך'; + + @override + String get insecureDevice => 'מכשיר בלתי מאובטח'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'אנחנו מצטערים, לא הצלחנו ליצור מפתחות מאובטחים על מכשיר זה.\n\nאנא הירשם ממכשיר אחר.'; + + @override + String get recoveryKeyCopiedToClipboard => 'מפתח השחזור הועתק ללוח'; + + @override + String get recoveryKey => 'מפתח שחזור'; + + @override + String get recoveryKeyOnForgotPassword => + 'אם אתה שוכח את הסיסמא שלך, הדרך היחידה שתוכל לשחזר את המידע שלך היא עם המפתח הזה.'; + + @override + String get recoveryKeySaveDescription => + 'אנחנו לא מאחסנים את המפתח הזה, אנא שמור את המפתח 24 מילים הזה במקום בטוח.'; + + @override + String get doThisLater => 'עשה זאת מאוחר יותר'; + + @override + String get saveKey => 'שמור מפתח'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'אין מפתח שחזור?'; + + @override + String get twoFactorAuthTitle => 'אימות דו-שלבי'; + + @override + String get enterCodeHint => + 'הכנס את הקוד בעל 6 ספרות מתוך\nאפליקציית האימות שלך'; + + @override + String get lostDeviceTitle => 'איבדת את המכשיר?'; + + @override + String get enterRecoveryKeyHint => 'הזן את מפתח השחזור שלך'; + + @override + String get recover => 'שחזר'; + + @override + String get loggingOut => 'מתנתק...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'האם את/ה בטוח/ה שאת/ה רוצה לצאת?'; + + @override + String get yesLogout => 'כן, התנתק'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'אשר'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => + 'כתובת דואר אלקטרוני זאת כבר נמצאת בשימוש'; + + @override + String emailChangedTo(String newEmail) { + return 'אימייל שונה ל-$newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => 'אימות נכשל, אנא נסה שוב'; + + @override + String get authenticationSuccessful => 'אימות הצליח!'; + + @override + String get sessionExpired => 'זמן החיבור הסתיים'; + + @override + String get incorrectRecoveryKey => 'מפתח שחזור שגוי'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => 'המפתח שחזור שהזנת שגוי'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'אימות דו-שלבי אופס בהצלחה'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'קוד האימות שלך פג תוקף'; + + @override + String get incorrectCode => 'קוד שגוי'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'אנו מתנצלים, אבל הקוד שהזנת איננו נכון'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_hi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_hi.dart new file mode 100644 index 0000000000..f45d282cb2 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_hi.dart @@ -0,0 +1,627 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hindi (`hi`). +class StringsLocalizationsHi extends StringsLocalizations { + StringsLocalizationsHi([String locale = 'hi']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'ठीक है'; + + @override + String get faq => 'अक्सर किये गए सवाल'; + + @override + String get contactSupport => 'सपोर्ट टीम से संपर्क करें'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'रद्द करें'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'बग रिपोर्ट करें'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'सत्यापित करें'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'कृपया प्रतीक्षा करें...'; + + @override + String get verifyPassword => 'पासवर्ड सत्यापित करें'; + + @override + String get incorrectPasswordTitle => 'ग़लत पासवर्ड'; + + @override + String get pleaseTryAgain => 'कृपया पुन: प्रयास करें'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'अपना पासवर्ड दर्ज करें'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'ओह'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'हटाएं'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'आपका पुनः स्वागत है!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'रिकवरी कुंजी का उपयोग करें'; + + @override + String get forgotPassword => 'पासवर्ड भूल गए'; + + @override + String get changeEmail => 'ईमेल बदलें'; + + @override + String get verifyEmail => 'ईमेल सत्यापित करें'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => + 'एन्क्रिप्शन कुंजियाँ उत्पन्न हो रही हैं...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'पुनःप्राप्ति कुंजी'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'दो-चरणीय प्रमाणीकरण |'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'लॉग आउट हो रहा है...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'ईमेल पहले से ही पंजीकृत है।'; + + @override + String get emailNotRegistered => 'ईमेल पंजीकृत नहीं है।'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'सत्र की अवधि समाप्त'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_hu.dart b/mobile/packages/strings/lib/l10n/strings_localizations_hu.dart new file mode 100644 index 0000000000..3ccc5091ae --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_hu.dart @@ -0,0 +1,630 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hungarian (`hu`). +class StringsLocalizationsHu extends StringsLocalizations { + StringsLocalizationsHu([String locale = 'hu']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Nem lehet csatlakozni az Ente-hez. Kérjük, ellenőrizze a hálózati beállításokat, és ha a hiba továbbra is fennáll, forduljon az ügyfélszolgálathoz.'; + + @override + String get networkConnectionRefusedErr => + 'Nem lehet csatlakozni az Ente-hez, próbálja újra egy idő után. Ha a hiba továbbra is fennáll, forduljon az ügyfélszolgálathoz.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Úgy tűnik, valami hiba történt. Kérjük, próbálja újra egy idő után. Ha a hiba továbbra is fennáll, forduljon ügyfélszolgálatunkhoz.'; + + @override + String get error => 'Hiba'; + + @override + String get ok => 'OK'; + + @override + String get faq => 'GY. I. K.'; + + @override + String get contactSupport => 'Lépj kapcsolatba az Ügyfélszolgálattal'; + + @override + String get emailYourLogs => 'E-mailben küldje el naplóit'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Kérjük, küldje el a naplókat erre az e-mail címre\n$toEmail'; + } + + @override + String get copyEmailAddress => 'E-mail cím másolása'; + + @override + String get exportLogs => 'Naplófájlok exportálása'; + + @override + String get cancel => 'Mégse'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Hiba bejelentése'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Csatlakozva a következőhöz: $endpoint'; + } + + @override + String get save => 'Mentés'; + + @override + String get send => 'Küldés'; + + @override + String get saveOrSendDescription => + 'El szeretné menteni ezt a tárhelyére (alapértelmezés szerint a Letöltések mappába), vagy elküldi más alkalmazásoknak?'; + + @override + String get saveOnlyDescription => + 'El szeretné menteni ezt a tárhelyére (alapértelmezés szerint a Letöltések mappába)?'; + + @override + String get enterNewEmailHint => 'Add meg az új e-mail címed'; + + @override + String get email => 'E-mail'; + + @override + String get verify => 'Hitelesítés'; + + @override + String get invalidEmailTitle => 'Érvénytelen e-mail cím'; + + @override + String get invalidEmailMessage => + 'Kérjük, adjon meg egy érvényes e-mail címet.'; + + @override + String get pleaseWait => 'Kérem várjon...'; + + @override + String get verifyPassword => 'Jelszó megerősítése'; + + @override + String get incorrectPasswordTitle => 'Érvénytelen jelszó'; + + @override + String get pleaseTryAgain => 'Kérjük, próbálja meg újra'; + + @override + String get enterPassword => 'Adja meg a jelszót'; + + @override + String get enterYourPasswordHint => 'Adja meg a jelszavát'; + + @override + String get activeSessions => 'Aktív munkamenetek'; + + @override + String get oops => 'Hoppá'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Hiba történt. Kérjük, próbálkozz újra'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'Ezzel kijelentkezik erről az eszközről!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'Ezzel kijelentkezik a következő eszközről:'; + + @override + String get terminateSession => 'Megszakítja a munkamenetet?'; + + @override + String get terminate => 'Befejezés'; + + @override + String get thisDevice => 'Ez az eszköz'; + + @override + String get createAccount => 'Felhasználó létrehozás'; + + @override + String get weakStrength => 'Gyenge'; + + @override + String get moderateStrength => 'Mérsékelt'; + + @override + String get strongStrength => 'Erős'; + + @override + String get deleteAccount => 'Fiók törlése'; + + @override + String get deleteAccountQuery => + 'Szomorúan tapasztaljuk. Problémába ütköztél?'; + + @override + String get yesSendFeedbackAction => 'Igen, visszajelzés küldése'; + + @override + String get noDeleteAccountAction => 'Fiók végleges törlése'; + + @override + String get initiateAccountDeleteTitle => + 'Kérjük, hitelesítse magát a fiók törlésének kezdeményezéséhez'; + + @override + String get confirmAccountDeleteTitle => 'Fiók törlésének megerősítése'; + + @override + String get confirmAccountDeleteMessage => + 'Ez a fiók össze van kapcsolva más Ente-alkalmazásokkal, ha használ ilyet.\n\nA feltöltött adataid törlését ütemezzük az összes Ente alkalmazásban, és a fiókod véglegesen törlésre kerül.'; + + @override + String get delete => 'Törlés'; + + @override + String get createNewAccount => 'Új fiók létrehozása'; + + @override + String get password => 'Jelszó'; + + @override + String get confirmPassword => 'Jelszó megerősítése'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Jelszó erőssége: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'Honnan hallottál Ente-ről? (opcionális)'; + + @override + String get hearUsExplanation => + 'Nem követjük nyomon az alkalmazástelepítéseket. Segítene, ha elmondaná, hol talált ránk!'; + + @override + String get signUpTerms => + 'Elfogadom az szolgáltatási feltételeket és az adatvédelmi irányelveket'; + + @override + String get termsOfServicesTitle => 'Használati feltételek'; + + @override + String get privacyPolicyTitle => 'Adatvédelmi irányelvek'; + + @override + String get ackPasswordLostWarning => + 'Tudomásul veszem, hogy ha elveszítem a jelszavamat, elveszíthetem az adataimat, mivel adataim végponttól végpontig titkosítva vannak.'; + + @override + String get encryption => 'Titkosítás'; + + @override + String get logInLabel => 'Bejelentkezés'; + + @override + String get welcomeBack => 'Köszöntjük ismét!'; + + @override + String get loginTerms => + 'A bejelentkezés gombra kattintva elfogadom az szolgáltatási feltételeket és az adatvédelmi irányelveket'; + + @override + String get noInternetConnection => 'Nincs internet kapcsolat'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Kérjük, ellenőrizze az internetkapcsolatát, és próbálja meg újra.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Az ellenőrzés sikertelen, próbálja újra'; + + @override + String get recreatePasswordTitle => 'Új jelszó létrehozása'; + + @override + String get recreatePasswordBody => + 'A jelenlegi eszköz nem elég erős a jelszavának ellenőrzéséhez, de újra tudjuk úgy generálni, hogy az minden eszközzel működjön.\n\nKérjük, jelentkezzen be helyreállítási kulcsával, és állítsa be újra jelszavát (ha szeretné, újra használhatja ugyanazt).'; + + @override + String get useRecoveryKey => 'Helyreállítási kulcs használata'; + + @override + String get forgotPassword => 'Elfelejtett jelszó'; + + @override + String get changeEmail => 'E-mail cím módosítása'; + + @override + String get verifyEmail => 'E-mail cím megerősítése'; + + @override + String weHaveSendEmailTo(String email) { + return 'E-mailt küldtünk a következő címre: $email'; + } + + @override + String get toResetVerifyEmail => + 'Jelszava visszaállításához először igazolja e-mail-címét.'; + + @override + String get checkInboxAndSpamFolder => + 'Kérjük, ellenőrizze beérkező leveleit (és spam mappát) az ellenőrzés befejezéséhez'; + + @override + String get tapToEnterCode => 'Koppintson a kód beírásához'; + + @override + String get sendEmail => 'E-mail küldése'; + + @override + String get resendEmail => 'E-mail újraküldése'; + + @override + String get passKeyPendingVerification => 'Az ellenőrzés még függőben van'; + + @override + String get loginSessionExpired => 'Lejárt a munkamenet'; + + @override + String get loginSessionExpiredDetails => + 'A munkameneted lejárt. Kérem lépjen be újra.'; + + @override + String get passkeyAuthTitle => 'Álkulcs megerősítése'; + + @override + String get waitingForVerification => 'Várakozás az ellenőrzésre...'; + + @override + String get tryAgain => 'Próbáld újra'; + + @override + String get checkStatus => 'Állapot ellenőrzése'; + + @override + String get loginWithTOTP => 'Bejelentkezés TOTP-vel'; + + @override + String get recoverAccount => 'Fiók visszaállítása'; + + @override + String get setPasswordTitle => 'Jelszó beállítás'; + + @override + String get changePasswordTitle => 'Jelszó módosítás'; + + @override + String get resetPasswordTitle => 'Jelszó visszaállítás'; + + @override + String get encryptionKeys => 'Titkosító kulcsok'; + + @override + String get enterPasswordToEncrypt => + 'Adjon meg egy jelszót, amellyel titkosíthatjuk adatait'; + + @override + String get enterNewPasswordToEncrypt => + 'Adjon meg egy új jelszót, amellyel titkosíthatjuk adatait'; + + @override + String get passwordWarning => + 'Ezt a jelszót nem tároljuk, így ha elfelejti, nem tudjuk visszafejteni adatait'; + + @override + String get howItWorks => 'Hogyan működik'; + + @override + String get generatingEncryptionKeys => 'Titkosító kulcsok generálása...'; + + @override + String get passwordChangedSuccessfully => + 'A jelszó sikeresen meg lett változtatva'; + + @override + String get signOutFromOtherDevices => 'Jelentkezzen ki más eszközökről'; + + @override + String get signOutOtherBody => + 'Ha úgy gondolja, hogy valaki ismeri jelszavát, kényszerítheti a fiókját használó összes többi eszközt a kijelentkezésre.'; + + @override + String get signOutOtherDevices => 'Jelentkezzen ki a többi eszközről'; + + @override + String get doNotSignOut => 'Ne jelentkezzen ki'; + + @override + String get generatingEncryptionKeysTitle => 'Titkosítási kulcs generálása...'; + + @override + String get continueLabel => 'Folytatás'; + + @override + String get insecureDevice => 'Nem biztonságos eszköz'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sajnáljuk, nem tudtunk biztonságos kulcsokat generálni ezen az eszközön.\n\nkérjük, regisztráljon egy másik eszközről.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'A helyreállítási kulcs a vágólapra másolva'; + + @override + String get recoveryKey => 'Visszaállítási kulcs'; + + @override + String get recoveryKeyOnForgotPassword => + 'Ha elfelejti jelszavát, csak ezzel a kulccsal tudja visszaállítani adatait.'; + + @override + String get recoveryKeySaveDescription => + 'Ezt a kulcsot nem tároljuk, kérjük, őrizze meg ezt a 24 szavas kulcsot egy biztonságos helyen.'; + + @override + String get doThisLater => 'Később'; + + @override + String get saveKey => 'Mentés'; + + @override + String get recoveryKeySaved => + 'A helyreállítási kulcs a Letöltések mappába mentve!'; + + @override + String get noRecoveryKeyTitle => 'Nincs helyreállítási kulcs?'; + + @override + String get twoFactorAuthTitle => 'Kétlépcsős hitelesítés (2FA)'; + + @override + String get enterCodeHint => + 'Írja be a 6 számjegyű kódot a hitelesítő alkalmazásból'; + + @override + String get lostDeviceTitle => 'Elveszett a készüléked?'; + + @override + String get enterRecoveryKeyHint => 'Visszaállító kód megadása'; + + @override + String get recover => 'Visszaállít'; + + @override + String get loggingOut => 'Kijelentkezés...'; + + @override + String get immediately => 'Azonnal'; + + @override + String get appLock => 'Alkalmazások zárolása'; + + @override + String get autoLock => 'Automatikus lezárás'; + + @override + String get noSystemLockFound => 'Nem található rendszerzár'; + + @override + String get deviceLockEnablePreSteps => + 'Az eszközzár engedélyezéséhez állítsa be az eszköz jelszavát vagy a zárképernyőt a rendszerbeállításokban.'; + + @override + String get appLockDescription => + 'Válasszon az eszköz alapértelmezett zárolási képernyője és a PIN-kóddal vagy jelszóval rendelkező egyéni zárolási képernyő között.'; + + @override + String get deviceLock => 'Eszköz lezárás'; + + @override + String get pinLock => 'PIN feloldás'; + + @override + String get autoLockFeatureDescription => + 'Az az idő, amely elteltével az alkalmazás zárolásra kerül, miután a háttérbe került'; + + @override + String get hideContent => 'Tartalom elrejtése'; + + @override + String get hideContentDescriptionAndroid => + 'Elrejti az alkalmazás tartalmát az alkalmazásváltóban, és letiltja a képernyőképeket'; + + @override + String get hideContentDescriptioniOS => + 'Elrejti az alkalmazás tartalmát az alkalmazásváltóban'; + + @override + String get tooManyIncorrectAttempts => 'Túl sok helytelen próbálkozás'; + + @override + String get tapToUnlock => 'Koppintson a feloldáshoz'; + + @override + String get areYouSureYouWantToLogout => 'Biztos benne, hogy kijelentkezik?'; + + @override + String get yesLogout => 'Igen, kijelentkezés'; + + @override + String get authToViewSecrets => + 'A titkos kulcsok megtekintéséhez hitelesítse magát'; + + @override + String get next => 'Következő'; + + @override + String get setNewPassword => 'Új jelszó beállítása'; + + @override + String get enterPin => 'PIN kód megadása'; + + @override + String get setNewPin => 'Új PIN kód beállítása'; + + @override + String get confirm => 'Megerősítés'; + + @override + String get reEnterPassword => 'Írja be újra a jelszót'; + + @override + String get reEnterPin => 'Írja be újra a PIN-kódot'; + + @override + String get androidBiometricHint => 'Személyazonosság ellenőrzése'; + + @override + String get androidBiometricNotRecognized => 'Nem felismerhető. Próbáld újra.'; + + @override + String get androidBiometricSuccess => 'Sikeres'; + + @override + String get androidCancelButton => 'Mégse'; + + @override + String get androidSignInTitle => 'Hitelesítés szükséges'; + + @override + String get androidBiometricRequiredTitle => 'Biometria szükséges'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Az eszköz hitelesítő adatai szükségesek'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Az eszköz hitelesítő adatai szükségesek'; + + @override + String get goToSettings => 'Beállítások megnyitása'; + + @override + String get androidGoToSettingsDescription => + 'A biometrikus hitelesítés nincs beállítva az eszközön. A biometrikus hitelesítés hozzáadásához lépjen a \'Beállítások > Biztonság\' menüpontra.'; + + @override + String get iOSLockOut => + 'A biometrikus hitelesítés ki van kapcsolva. Az engedélyezéséhez zárja le és oldja fel a képernyőt.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Ez az e-mai cím már regisztrálva van.'; + + @override + String get emailNotRegistered => 'Ez az e-mail cím nincs regisztrálva.'; + + @override + String get thisEmailIsAlreadyInUse => 'Ez az e-mail már használatban van'; + + @override + String emailChangedTo(String newEmail) { + return 'Az e-mail cím módosítva erre: $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'A hitelesítés sikertelen, próbálja újra'; + + @override + String get authenticationSuccessful => 'Sikeres hitelesítés!'; + + @override + String get sessionExpired => 'A munkamenet lejárt'; + + @override + String get incorrectRecoveryKey => 'Helytelen helyreállítási kulcs'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'A megadott helyreállítási kulcs helytelen'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'A kétfaktoros hitelesítés visszaállítása sikeres'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'Ez az ellenőrző kód lejárt'; + + @override + String get incorrectCode => 'Helytelen kód'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sajnáljuk, a megadott kód helytelen'; + + @override + String get developerSettings => 'Fejlesztői beállítások'; + + @override + String get serverEndpoint => 'Szerver végpont'; + + @override + String get invalidEndpoint => 'Érvénytelen végpont'; + + @override + String get invalidEndpointMessage => + 'Sajnáljuk, a megadott végpont érvénytelen. Adjon meg egy érvényes végpontot, és próbálja újra.'; + + @override + String get endpointUpdatedMessage => 'A végpont sikeresen frissítve'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index 75aceba5c0..e3839340a6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -14,40 +14,40 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Sepertinya ada yang salah. Mohon coba lagi setelah beberapa waktu. Jika galat masih ada, Anda dapat menghubungi tim bantuan kami.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Sepertinya ada yang salah. Mohon coba lagi setelah beberapa waktu. Jika galat masih ada, Anda dapat menghubungi tim bantuan kami.'; @override - String get error => 'Error'; + String get error => 'Kesalahan'; @override - String get ok => 'Ok'; + String get ok => 'Oke'; @override - String get faq => 'FAQ'; + String get faq => 'Pertanyaan yang sering ditanyakan'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Hubungi dukungan'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Kirimkan log Anda melalui surel'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Mohon kirim log ke $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Salin alamat surel'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Ekspor log'; @override - String get cancel => 'Cancel'; + String get cancel => 'Batal'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsId extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Laporkan bug'; @override String get logsDialogBody => @@ -100,505 +88,509 @@ class StringsLocalizationsId extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Terhubung ke $endpoint'; } @override - String get save => 'Save'; + String get save => 'Simpan'; @override - String get send => 'Send'; + String get send => 'Kirim'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads) atau Anda ingin kirimkan ke aplikasi lain?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads)'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Masukkan alamat email baru anda'; @override String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Verifikasi'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Alamat email tidak valid'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Harap masukkan alamat email yang valid.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Mohon tunggu...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verifikasi kata sandi'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Kata sandi salah'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Harap coba lagi'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Masukkan kata sandi'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Masukkan kata sandi Anda'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sesi aktif'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Ada yang salah. Mohon coba kembali'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Langkah ini akan mengeluarkan Anda dari gawai ini!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Langkah ini akan mengeluarkan Anda dari gawai berikut:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Akhiri sesi?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Akhiri'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Gawai ini'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Buat akun'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Lemah'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Sedang'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Kuat'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Hapus akun'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Kami akan merasa kehilangan Anda. Apakah Anda menghadapi masalah?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ya, kirim umpan balik'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Tidak, hapus akun'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Harap autentikasi untuk memulai penghapusan akun'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Konfirmasikan penghapusan akun'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Akun ini terhubung dengan aplikasi Ente yang lain (jika Anda pakai).\n\nData yang Anda unggah di seluruh aplikasi Ente akan dijadwalkan untuk dihapus. Akun Anda juga akan dihapus secara permanen.'; @override - String get delete => 'Delete'; + String get delete => 'Hapus'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Buat akun baru'; @override - String get password => 'Password'; + String get password => 'Kata Sandi'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Konfirmasi kata sandi'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Tingkat kekuatan kata sandi: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Dari mana Anda menemukan Ente? (opsional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Kami tidak melacak penginstalan aplikasi kami. Akan sangat membantu kami bila Anda memberitahu kami dari mana Anda mengetahui Ente!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Ketentuan'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Kebijakan Privasi'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Saya mengerti bahwa jika saya lupa kata sandi saya, data saya dapat hilang karena data saya terenkripsi secara end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Enkripsi'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Masuk akun'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Selamat datang kembali!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Dengan menekan masuk akun, saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Tiada koneksi internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Mohon periksa koneksi internet Anda dan coba kembali.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Gagal memverifikasi. Mohon coba lagi'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Membuat kembali kata sandi'; @override String get recreatePasswordBody => - '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).'; + 'Gawai Anda saat ini tidak dapat memverifikasi kata sandi Anda. Namun, kami dapat membuat ulang dengan cara yang dapat digunakan pada semua gawai.\n\nMohon masuk log dengan kunci pemulihan dan buat ulang kata sandi Anda (kata sandi yang sama diperbolehkan).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Gunakan kunci pemulihan'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Lupa kata sandi'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Ubah alamat email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verifikasi email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Kami telah mengirimkan sebuah posel ke $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Untuk mengatur ulang kata sandi, mohon verifikasi surel Anda terlebih dahulu.'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => 'Mohon cek'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Ketuk untuk memasukkan kode'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Kirim surel'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Kirim ulang surel'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verifikasi tertunda'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sesi sudah berakhir'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Sesi Anda sudah berakhir. Mohon masuk log kembali.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verifikasi passkey'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Menantikan verifikasi...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Coba lagi'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Periksa status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Masuk menggunakan TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Pulihkan akun'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Atur kata sandi'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Ubah kata sandi'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Atur ulang kata sandi'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Kunci enkripsi'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Masukkan kata sandi yang dapat kami gunakan untuk mengenkripsi data Anda'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Masukkan kata sandi baru yang dapat kami gunakan untuk mengenkripsi data Anda'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Kami tidak menyimpan kata sandi Anda. Jika Anda lupa, kami tidak dapat mendekripsi data Anda'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cara kerjanya'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Sedang membuat kunci enkripsi...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Kata sandi sukses diubah'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Keluar dari gawai yang lain'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jika Anda pikir seseorang mungkin mengetahui kata sandi Anda, Anda dapat mengeluarkan akun Anda pada semua gawai'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Keluar akun pada gawai yang lain'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Jangan keluar'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Sedang membuat kunci enkripsi...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Lanjutkan'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Perangkat tidak aman'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Maaf, kami tidak dapat membuat kunci yang aman pada perangkat ini.\n\nHarap mendaftar dengan perangkat lain.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Kunci pemulihan disalin ke papan klip'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kunci pemulihan'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jika Anda lupa kata sandi, satu-satunya cara memulihkan data Anda adalah dengan kunci ini.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Kami tidak menyimpan kunci ini, jadi harap simpan kunci yang berisi 24 kata ini dengan aman.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Lakukan lain kali'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Simpan kunci'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kunci pemulihan sudah tersimpan di folder \'Downloads\'!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Tidak punya kunci pemulihan?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autentikasi dua langkah'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Masukkan kode 6 digit dari aplikasi autentikator Anda'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Perangkat hilang?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Masukkan kunci pemulihan Anda'; @override - String get recover => 'Recover'; + String get recover => 'Pulihkan'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Mengeluarkan akun...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Segera'; @override - String get appLock => 'App lock'; + String get appLock => 'Kunci aplikasi'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Kunci otomatis'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Tidak ditemukan kunci sistem'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Pasang kunci sandi atau kunci layar pada pengaturan sistem untuk menyalakan Pengunci Gawai.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Pilih layar kunci bawaan gawai Anda ATAU layar kunci kustom dengan PIN atau kata sandi.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Kunci perangkat'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Durasi waktu aplikasi akan terkunci setelah aplikasi ditutup'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Sembunyikan isi'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Menyembunyikan konten aplikasi di pemilih aplikasi dan menonaktifkan tangkapan layar'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Menyembunyikan konten aplikasi di pemilih aplikasi'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Terlalu banyak percobaan yang salah'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Ketuk untuk membuka'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Anda yakin ingin keluar dari akun ini?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ya, keluar akun'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Harap lakukan autentikasi untuk melihat rahasia Anda'; @override - String get next => 'Next'; + String get next => 'Selanjutnya'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Pasang kata sandi baru'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Masukkan PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Pasang PIN yang baru'; @override - String get confirm => 'Confirm'; + String get confirm => 'Konfirmasikan'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Masukkan kembali kata sandi'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Masukkan kembali PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Verifikasikan identitas Anda'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Tidak dikenal. Coba lagi.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Sukses'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Batalkan'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Autentikasi diperlukan'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Biometrik diperlukan'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Kredensial perangkat diperlukan'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Kredensial perangkat diperlukan'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Pergi ke pengaturan'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Tidak ada autentikasi biometrik pada gawai Anda. Buka \'Pengaturan > Keamanan\' untuk menambahkan autentikasi biometrik pada gawai Anda.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Autentikasi biometrik dimatikan. Kunci dan buka layar Anda untuk menyalakan autentikasi biometrik.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Oke'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Email sudah terdaftar.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Email belum terdaftar.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Surel ini sudah dipakai!'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Surel sudah diganti menjadi $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Gagal mengautentikasi. Mohon coba lagi'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Sukses mengautentikasi!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Sesi berakhir'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Kunci pemulihan takbenar'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Kunci pemulihan yang Anda masukkan takbenar'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Autentikasi dwifaktor sukses diatur ulang'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +603,28 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Kode verifikasi Anda telah kedaluwarsa'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Kode takbenar'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Maaf, kode yang Anda masukkan takbenar'; + + @override + String get developerSettings => 'Pengaturan Pengembang'; + + @override + String get serverEndpoint => 'Peladen endpoint'; + + @override + String get invalidEndpoint => 'Endpoint takvalid'; + + @override + String get invalidEndpointMessage => + 'Maaf, endpoint yang Anda masukkan takvalid. Mohon masukkan endpoint yang valid, lalu coba kembali.'; + + @override + String get endpointUpdatedMessage => 'Endpoint berhasil diubah'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_it.dart b/mobile/packages/strings/lib/l10n/strings_localizations_it.dart new file mode 100644 index 0000000000..9a13c0cd7c --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_it.dart @@ -0,0 +1,634 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Italian (`it`). +class StringsLocalizationsIt extends StringsLocalizations { + StringsLocalizationsIt([String locale = 'it']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Impossibile connettersi a Ente, controlla le impostazioni di rete e contatta l\'assistenza se l\'errore persiste.'; + + @override + String get networkConnectionRefusedErr => + 'Impossibile connettersi a Ente, riprova tra un po\' di tempo. Se l\'errore persiste, contatta l\'assistenza.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Sembra che qualcosa sia andato storto. Riprova tra un po\'. Se l\'errore persiste, contatta il nostro team di supporto.'; + + @override + String get error => 'Errore'; + + @override + String get ok => 'OK'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contatta il supporto'; + + @override + String get emailYourLogs => 'Invia una mail dei tuoi log'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Invia i log a \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copia indirizzo email'; + + @override + String get exportLogs => 'Esporta log'; + + @override + String get cancel => 'Annulla'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Segnala un problema'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connesso a $endpoint'; + } + + @override + String get save => 'Salva'; + + @override + String get send => 'Invia'; + + @override + String get saveOrSendDescription => + 'Vuoi salvarlo nel tuo spazio di archiviazione (cartella Download per impostazione predefinita) o inviarlo ad altre applicazioni?'; + + @override + String get saveOnlyDescription => + 'Vuoi salvarlo nel tuo spazio di archiviazione (cartella Download per impostazione predefinita)?'; + + @override + String get enterNewEmailHint => 'Inserisci il tuo nuovo indirizzo email'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verifica'; + + @override + String get invalidEmailTitle => 'Indirizzo email non valido'; + + @override + String get invalidEmailMessage => 'Inserisci un indirizzo email valido.'; + + @override + String get pleaseWait => 'Attendere prego...'; + + @override + String get verifyPassword => 'Verifica la password'; + + @override + String get incorrectPasswordTitle => 'Password sbagliata'; + + @override + String get pleaseTryAgain => 'Per favore riprova'; + + @override + String get enterPassword => 'Inserisci la password'; + + @override + String get enterYourPasswordHint => 'Inserisci la tua password'; + + @override + String get activeSessions => 'Sessioni attive'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Qualcosa è andato storto, per favore riprova'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'Questo ti disconnetterà da questo dispositivo!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'Questo ti disconnetterà dal seguente dispositivo:'; + + @override + String get terminateSession => 'Termina sessione?'; + + @override + String get terminate => 'Termina'; + + @override + String get thisDevice => 'Questo dispositivo'; + + @override + String get createAccount => 'Crea account'; + + @override + String get weakStrength => 'Debole'; + + @override + String get moderateStrength => 'Mediocre'; + + @override + String get strongStrength => 'Forte'; + + @override + String get deleteAccount => 'Elimina account'; + + @override + String get deleteAccountQuery => + 'Ci dispiace vederti andare via. Stai avendo qualche problema?'; + + @override + String get yesSendFeedbackAction => 'Sì, invia un feedback'; + + @override + String get noDeleteAccountAction => 'No, elimina l\'account'; + + @override + String get initiateAccountDeleteTitle => + 'Si prega di autenticarsi per avviare l\'eliminazione dell\'account'; + + @override + String get confirmAccountDeleteTitle => + 'Conferma l\'eliminazione dell\'account'; + + @override + String get confirmAccountDeleteMessage => + 'Questo account è collegato ad altre app di Ente, se ne utilizzi.\n\nI tuoi dati caricati, su tutte le app di Ente, saranno pianificati per la cancellazione e il tuo account verrà eliminato definitivamente.'; + + @override + String get delete => 'Cancella'; + + @override + String get createNewAccount => 'Crea un nuovo account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Conferma la password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Forza password: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => + 'Dove hai sentito parlare di Ente? (opzionale)'; + + @override + String get hearUsExplanation => + 'Non teniamo traccia delle installazioni dell\'app. Sarebbe utile se ci dicessi dove ci hai trovato!'; + + @override + String get signUpTerms => + 'Accetto i termini di servizio e la politica sulla privacy'; + + @override + String get termsOfServicesTitle => 'Termini'; + + @override + String get privacyPolicyTitle => 'Politica sulla Privacy'; + + @override + String get ackPasswordLostWarning => + 'Comprendo che se perdo la password, potrei perdere l\'accesso ai miei dati poiché i miei dati sono criptati end-to-end.'; + + @override + String get encryption => 'Crittografia'; + + @override + String get logInLabel => 'Accedi'; + + @override + String get welcomeBack => 'Bentornato!'; + + @override + String get loginTerms => + 'Cliccando sul pulsante Accedi, accetto i termini di servizio e la politica sulla privacy'; + + @override + String get noInternetConnection => 'Nessuna connessione internet'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Si prega di verificare la propria connessione Internet e riprovare.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verifica fallita, per favore riprova'; + + @override + String get recreatePasswordTitle => 'Ricrea password'; + + @override + String get recreatePasswordBody => + 'Il dispositivo attuale non è abbastanza potente per verificare la tua password, ma la possiamo rigenerare in un modo che funzioni su tutti i dispositivi.\n\nEffettua il login utilizzando la tua chiave di recupero e rigenera la tua password (puoi utilizzare nuovamente la stessa se vuoi).'; + + @override + String get useRecoveryKey => 'Utilizza un codice di recupero'; + + @override + String get forgotPassword => 'Password dimenticata'; + + @override + String get changeEmail => 'Modifica email'; + + @override + String get verifyEmail => 'Verifica email'; + + @override + String weHaveSendEmailTo(String email) { + return 'Abbiamo inviato una mail a $email'; + } + + @override + String get toResetVerifyEmail => + 'Per reimpostare la tua password, prima verifica la tua email.'; + + @override + String get checkInboxAndSpamFolder => + 'Per favore, controlla la tua casella di posta (e lo spam) per completare la verifica'; + + @override + String get tapToEnterCode => 'Tocca per inserire il codice'; + + @override + String get sendEmail => 'Invia email'; + + @override + String get resendEmail => 'Rinvia email'; + + @override + String get passKeyPendingVerification => 'La verifica è ancora in corso'; + + @override + String get loginSessionExpired => 'Sessione scaduta'; + + @override + String get loginSessionExpiredDetails => + 'La sessione è scaduta. Si prega di accedere nuovamente.'; + + @override + String get passkeyAuthTitle => 'Verifica della passkey'; + + @override + String get waitingForVerification => 'In attesa di verifica...'; + + @override + String get tryAgain => 'Riprova'; + + @override + String get checkStatus => 'Verifica stato'; + + @override + String get loginWithTOTP => 'Login con TOTP'; + + @override + String get recoverAccount => 'Recupera account'; + + @override + String get setPasswordTitle => 'Imposta password'; + + @override + String get changePasswordTitle => 'Modifica password'; + + @override + String get resetPasswordTitle => 'Reimposta password'; + + @override + String get encryptionKeys => 'Chiavi di crittografia'; + + @override + String get enterPasswordToEncrypt => + 'Inserisci una password per criptare i tuoi dati'; + + @override + String get enterNewPasswordToEncrypt => + 'Inserisci una nuova password per criptare i tuoi dati'; + + @override + String get passwordWarning => + 'Non memorizziamo questa password, quindi se te la dimentichi, non possiamo decriptare i tuoi dati'; + + @override + String get howItWorks => 'Come funziona'; + + @override + String get generatingEncryptionKeys => + 'Generazione delle chiavi di crittografia...'; + + @override + String get passwordChangedSuccessfully => 'Password modificata con successo'; + + @override + String get signOutFromOtherDevices => 'Esci dagli altri dispositivi'; + + @override + String get signOutOtherBody => + 'Se pensi che qualcuno possa conoscere la tua password, puoi forzare tutti gli altri dispositivi che usano il tuo account ad uscire.'; + + @override + String get signOutOtherDevices => 'Esci dagli altri dispositivi'; + + @override + String get doNotSignOut => 'Non uscire'; + + @override + String get generatingEncryptionKeysTitle => + 'Generazione delle chiavi di crittografia...'; + + @override + String get continueLabel => 'Continua'; + + @override + String get insecureDevice => 'Dispositivo non sicuro'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Siamo spiacenti, non possiamo generare le chiavi sicure su questo dispositivo.\n\nPer favore, accedi da un altro dispositivo.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Chiave di recupero copiata negli appunti'; + + @override + String get recoveryKey => 'Chiave di recupero'; + + @override + String get recoveryKeyOnForgotPassword => + 'Se dimentichi la password, l\'unico modo per recuperare i tuoi dati è con questa chiave.'; + + @override + String get recoveryKeySaveDescription => + 'Non memorizziamo questa chiave, per favore salva questa chiave di 24 parole in un posto sicuro.'; + + @override + String get doThisLater => 'Fallo più tardi'; + + @override + String get saveKey => 'Salva chiave'; + + @override + String get recoveryKeySaved => + 'Chiave di recupero salvata nella cartella Download!'; + + @override + String get noRecoveryKeyTitle => 'Nessuna chiave di recupero?'; + + @override + String get twoFactorAuthTitle => 'Autenticazione a due fattori'; + + @override + String get enterCodeHint => + 'Inserisci il codice di 6 cifre dalla tua app di autenticazione'; + + @override + String get lostDeviceTitle => 'Dispositivo perso?'; + + @override + String get enterRecoveryKeyHint => 'Inserisci la tua chiave di recupero'; + + @override + String get recover => 'Recupera'; + + @override + String get loggingOut => 'Disconnessione...'; + + @override + String get immediately => 'Immediatamente'; + + @override + String get appLock => 'Blocco app'; + + @override + String get autoLock => 'Blocco automatico'; + + @override + String get noSystemLockFound => 'Nessun blocco di sistema trovato'; + + @override + String get deviceLockEnablePreSteps => + 'Per attivare il blocco del dispositivo, impostare il codice di accesso o il blocco dello schermo nelle impostazioni del sistema.'; + + @override + String get appLockDescription => + 'Scegli tra la schermata di blocco predefinita del dispositivo e una schermata di blocco personalizzata con PIN o password.'; + + @override + String get deviceLock => 'Blocco del dispositivo'; + + @override + String get pinLock => 'Blocco con PIN'; + + @override + String get autoLockFeatureDescription => + 'Tempo dopo il quale l\'applicazione si blocca dopo essere stata messa in background'; + + @override + String get hideContent => 'Nascondi il contenuto'; + + @override + String get hideContentDescriptionAndroid => + 'Nasconde il contenuto nel selettore delle app e disabilita gli screenshot'; + + @override + String get hideContentDescriptioniOS => + 'Nasconde il contenuto nel selettore delle app'; + + @override + String get tooManyIncorrectAttempts => 'Troppi tentativi errati'; + + @override + String get tapToUnlock => 'Tocca per sbloccare'; + + @override + String get areYouSureYouWantToLogout => + 'Sei sicuro di volerti disconnettere?'; + + @override + String get yesLogout => 'Si, effettua la disconnessione'; + + @override + String get authToViewSecrets => 'Autenticati per visualizzare i tuoi segreti'; + + @override + String get next => 'Successivo'; + + @override + String get setNewPassword => 'Imposta una nuova password'; + + @override + String get enterPin => 'Inserisci PIN'; + + @override + String get setNewPin => 'Imposta un nuovo PIN'; + + @override + String get confirm => 'Conferma'; + + @override + String get reEnterPassword => 'Reinserisci la password'; + + @override + String get reEnterPin => 'Reinserisci il PIN'; + + @override + String get androidBiometricHint => 'Verifica l\'identità'; + + @override + String get androidBiometricNotRecognized => 'Non riconosciuto. Riprova.'; + + @override + String get androidBiometricSuccess => 'Successo'; + + @override + String get androidCancelButton => 'Annulla'; + + @override + String get androidSignInTitle => 'Autenticazione necessaria'; + + @override + String get androidBiometricRequiredTitle => + 'Autenticazione biometrica richiesta'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Credenziali del dispositivo richieste'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Credenziali del dispositivo richieste'; + + @override + String get goToSettings => 'Vai alle impostazioni'; + + @override + String get androidGoToSettingsDescription => + 'L\'autenticazione biometrica non è impostata sul tuo dispositivo. Vai a \'Impostazioni > Sicurezza\' per impostarla.'; + + @override + String get iOSLockOut => + 'L\'autenticazione biometrica è disabilitata. Blocca e sblocca lo schermo per abilitarla.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email già registrata.'; + + @override + String get emailNotRegistered => 'Email non registrata.'; + + @override + String get thisEmailIsAlreadyInUse => 'Questa email é già in uso'; + + @override + String emailChangedTo(String newEmail) { + return 'Email modificata in $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Autenticazione non riuscita, riprova'; + + @override + String get authenticationSuccessful => 'Autenticazione riuscita!'; + + @override + String get sessionExpired => 'Sessione scaduta'; + + @override + String get incorrectRecoveryKey => 'Chiave di recupero errata'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'La chiave di recupero che hai inserito non è corretta'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Autenticazione a due fattori ripristinata con successo'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Il tuo codice di verifica è scaduto'; + + @override + String get incorrectCode => 'Codice errato'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Spiacenti, il codice che hai inserito non è corretto'; + + @override + String get developerSettings => 'Impostazioni sviluppatore'; + + @override + String get serverEndpoint => 'Endpoint del server'; + + @override + String get invalidEndpoint => 'Endpoint invalido'; + + @override + String get invalidEndpointMessage => + 'Spiacenti, l\'endpoint inserito non è valido. Inserisci un endpoint valido e riprova.'; + + @override + String get endpointUpdatedMessage => 'Endpoint aggiornato con successo'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 007a5eafdd..54766bcdb1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -14,11 +14,11 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Enteに接続できません。しばらくしてから再試行してください。エラーが継続する場合は、サポートにお問い合わせください。'; + 'Enteに接続できませんでした。しばらくしてから再試行してください。エラーが解決しない場合は、サポートにお問い合わせください。'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - '問題が発生したようです。しばらくしてから再試行してください。エラーが継続する場合は、サポートチームにお問い合わせください。'; + '問題が発生したようです。しばらくしてから再試行してください。エラーが解決しない場合は、サポートチームにお問い合わせください。'; @override String get error => 'エラー'; @@ -30,24 +30,24 @@ class StringsLocalizationsJa extends StringsLocalizations { String get faq => 'FAQ'; @override - String get contactSupport => 'サポートに連絡する'; + String get contactSupport => 'サポートに問い合わせ'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'ログをメールで送信'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'ログを以下のアドレスに送信してください \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'メールアドレスをコピー'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'ログのエクスポート'; @override - String get cancel => 'Cancel'; + String get cancel => 'キャンセル'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsJa extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'バグを報告'; @override String get logsDialogBody => @@ -100,505 +88,483 @@ class StringsLocalizationsJa extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return '$endpoint に接続しました'; } @override - String get save => 'Save'; + String get save => '保存'; @override - String get send => 'Send'; + String get send => '送信'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'これをストレージ (デフォルトではダウンロードフォルダ) に保存しますか、もしくは他のアプリに送信しますか?'; @override - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + String get saveOnlyDescription => 'これをストレージに保存しますか? (デフォルトではダウンロードフォルダに保存)'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'E メール'; @override - String get verify => 'Verify'; + String get verify => '認証'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'メールアドレスが無効です'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => '有効なメールアドレスを入力して下さい'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'お待ちください...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'パスワードを確認'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'パスワードが正しくありません'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => '再度お試しください'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'パスワードを入力'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'パスワードを入力してください'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'アクティブセッション'; @override - String get oops => 'Oops'; + String get oops => 'おっと'; @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + String get somethingWentWrongPleaseTryAgain => '問題が発生しました、再試行してください'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => 'このデバイスからログアウトします!'; @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + String get thisWillLogYouOutOfTheFollowingDevice => '以下のデバイスからログアウトします:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'セッションを終了しますか?'; @override - String get terminate => 'Terminate'; + String get terminate => '終了'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'このデバイス'; @override - String get createAccount => 'Create account'; + String get createAccount => 'アカウント作成'; @override - String get weakStrength => 'Weak'; + String get weakStrength => '脆弱'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'まあまあ'; @override - String get strongStrength => 'Strong'; + String get strongStrength => '強力'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'アカウント削除'; @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + String get deleteAccountQuery => 'ご不便をおかけして申し訳ありません。なにか問題が発生していますか?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'はい、フィードバックを送信します'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'いいえ、アカウントを削除します'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => 'アカウントの削除を開始するためには認証が必要です'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'アカウントの削除に同意'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'このアカウントは他のEnteアプリも使用している場合はそれらにも紐づけされています。\nすべてのEnteアプリでアップロードされたデータは削除され、アカウントは完全に削除されます。'; @override - String get delete => 'Delete'; + String get delete => '削除'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => '新規アカウント作成'; @override - String get password => 'Password'; + String get password => 'パスワード'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'パスワードの確認'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'パスワードの強度: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ente についてどのようにお聞きになりましたか?(任意)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + '私たちはアプリのインストールを追跡していません。私たちをお知りになった場所を教えてください!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + '利用規約プライバシー ポリシーに同意します'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => '利用規約'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'プライバシーポリシー'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + '私のデータはエンドツーエンドで暗号化されるため、パスワードを紛失した場合、データが失われる可能性があることを理解しています。'; @override - String get encryption => 'Encryption'; + String get encryption => '暗号化'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'ログイン'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'おかえりなさい!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'ログインをクリックする場合、利用規約およびプライバシー ポリシーに同意するものとします。'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'インターネット接続なし'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'インターネット接続を確認して、再試行してください。'; @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + String get verificationFailedPleaseTryAgain => '確認に失敗しました、再試行してください'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'パスワードを再設定'; @override String get recreatePasswordBody => - '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).'; + '現在のデバイスはパスワードを確認するのには不十分ですが、すべてのデバイスで動作するように再生成することはできます。\n\n回復キーを使用してログインし、パスワードを再生成してください(ご希望の場合は同じものを再度使用できます)。'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => '回復キーを使用'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'パスワードを忘れた場合'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'メールアドレスを変更'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'メールアドレス認証'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '$email にメールを送信しました'; } @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + String get toResetVerifyEmail => 'パスワードをリセットするには、メールの確認を先に行ってください。'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => '受信トレイ(および迷惑メール)を確認して認証を完了してください'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'タップしてコードを入力'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'メール送信'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'メールを再送信'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => '認証はまだ保留中です'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'セッションの有効期限が切れました'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => 'セッションの有効期限が切れました。再度ログインしてください。'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'パスキー認証'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => '認証を待っています...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => '再試行'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'ステータスの確認'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'TOTPでログイン'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'アカウントを回復'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'パスワードの設定'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'パスワードの変更'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'パスワードのリセット'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => '暗号鍵'; @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + String get enterPasswordToEncrypt => 'データの暗号化に使用するパスワードを入力してください'; @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + String get enterNewPasswordToEncrypt => 'データの暗号化に使用する新しいパスワードを入力してください'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + '私たちはこのパスワードを保存していないので、あなたがそれを忘れた場合に私たちがあなたのデータを代わりに復号することはできません'; @override - String get howItWorks => 'How it works'; + String get howItWorks => '動作の仕組み'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => '暗号鍵を生成中...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'パスワードを変更しました'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => '他のデバイスからサインアウトする'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + '他の誰かがあなたのパスワードを知っている可能性があると判断した場合は、あなたのアカウントを使用している他のすべてのデバイスから強制的にサインアウトできます。'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => '他のデバイスからサインアウトする'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'サインアウトしない'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => '暗号化鍵を生成中...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => '続行'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => '安全ではないデバイス'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '申し訳ありませんが、このデバイスでは安全な鍵を生成できませんでした。\n\n別のデバイスから登録してください。'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => '回復キーをクリップボードにコピーしました'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => '回復キー'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'パスワードを忘れた場合、データを回復できる唯一の方法がこのキーです。'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + '私たちはこのキーを保存しません。この 24 単語のキーを安全な場所に保存してください。'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => '後で行う'; @override - String get saveKey => 'Save key'; + String get saveKey => 'キーを保存'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => 'リカバリキーをダウンロードフォルダに保存しました!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '回復キーがありませんか?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => '2 要素認証'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => '認証アプリに表示された 6 桁のコードを入力してください'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'デバイスを紛失しましたか?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => '回復キーを入力'; @override - String get recover => 'Recover'; + String get recover => '回復'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'ログアウト中...'; @override - String get immediately => 'Immediately'; + String get immediately => 'すぐに'; @override - String get appLock => 'App lock'; + String get appLock => 'アプリのロック'; @override - String get autoLock => 'Auto lock'; + String get autoLock => '自動ロック'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'システムロックが見つかりませんでした'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + '端末のロックを有効にするには、システム設定で端末のパスコードまたは画面ロックを設定してください。'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + '端末のデフォルトのロック画面と、PINまたはパスワードを使用したカスタムロック画面を選択します。'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => '生体認証'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'PIN'; @override - String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + String get autoLockFeatureDescription => 'アプリをバックグラウンドでロックするまでの時間'; @override - String get hideContent => 'Hide content'; + String get hideContent => '内容を非表示'; @override - String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + String get hideContentDescriptionAndroid => 'アプリの内容を非表示にし、スクリーンショットを無効にします'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'アプリを切り替えた際に、アプリの内容を非表示にします'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => '間違った回数が多すぎます'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'タップして解除'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => '本当にログアウトしてよろしいですか?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'はい、ログアウトします'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => '秘密鍵を閲覧するためには認証が必要です'; @override - String get next => 'Next'; + String get next => '次へ'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => '新しいパスワードを設定'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'PINを入力してください'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => '新しいPINを設定'; @override - String get confirm => 'Confirm'; + String get confirm => '確認'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'パスワードを再入力してください'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'PINを再入力してください'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => '本人を確認する'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => '認識できません。再試行してください。'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => '成功'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'キャンセル'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => '認証が必要です'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => '生体認証が必要です'; @override - String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + String get androidDeviceCredentialsRequiredTitle => 'デバイスの認証情報が必要です'; @override - String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + String get androidDeviceCredentialsSetupDescription => 'デバイスの認証情報が必要です'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => '設定を開く'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + '生体認証がデバイスで設定されていません。生体認証を追加するには、\"設定 > セキュリティ\"を開いてください。'; @override - String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + String get iOSLockOut => '生体認証が無効化されています。画面をロック・ロック解除して生体認証を有効化してください。'; @override String get iOSOkButton => 'OK'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'メールアドレスはすでに登録されています。'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'メールアドレスはまだ登録されていません。'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'このアドレスは既に使用されています'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'メールアドレスが $newEmail に変更されました'; } @override - String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + String get authenticationFailedPleaseTryAgain => '認証に失敗しました、再試行してください'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => '認証に成功しました!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'セッションが失効しました'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => '不正な回復キー'; @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + String get theRecoveryKeyYouEnteredIsIncorrect => '入力された回復キーは正しくありません'; @override - String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + String get twofactorAuthenticationSuccessfullyReset => '2 要素認証は正常にリセットされました'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +576,28 @@ class StringsLocalizationsJa extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => '確認コードが失効しました'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => '不正なコード'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + '申し訳ありませんが、入力されたコードは正しくありません'; + + @override + String get developerSettings => '開発者向け設定'; + + @override + String get serverEndpoint => 'サーバーエンドポイント'; + + @override + String get invalidEndpoint => '無効なエンドポイントです'; + + @override + String get invalidEndpointMessage => + '入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。'; + + @override + String get endpointUpdatedMessage => 'エンドポイントの更新に成功しました'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ka.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ka.dart new file mode 100644 index 0000000000..3470bb7b33 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ka.dart @@ -0,0 +1,629 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Georgian (`ka`). +class StringsLocalizationsKa extends StringsLocalizations { + StringsLocalizationsKa([String locale = 'ka']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'მხარდაჭერის გუნდთან დაკავშირება'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'გაუქმება'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'პრობლემის შესახებ შეტყობინება'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'ელექტრონული ფოსტა'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'გთხოვთ, დაელოდოთ...'; + + @override + String get verifyPassword => 'პაროლის დასტური'; + + @override + String get incorrectPasswordTitle => 'არასწორი პაროლი'; + + @override + String get pleaseTryAgain => 'გთხოვთ, სცადოთ ხელახლა'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'წაშლა'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => + 'როგორ შეიტყვეთ Ente-ს შესახებ? (არასავალდებულო)'; + + @override + String get hearUsExplanation => + 'ჩვენ არ ვაკონტროლებთ აპლიკაციის ინსტალაციას. სასარგებლო იქნებოდა, თუ გვეტყოდით, სად გვიპოვეთ!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'კეთილი იყოს თქვენი დაბრუნება!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'ინტერნეტთან კავშირი არ არის'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'გთხოვთ, შეამოწმოთ თქვენი ინტერნეტ კავშირი და სცადოთ ხელახლა.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'ელექტრონული ფოსტის შეცვლა'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'ყველა მოწყობილობიდან გამოსვლა'; + + @override + String get signOutOtherBody => + 'თუ ფიქრობთ, რომ ვინმემ შესაძლოა იცოდეს თქვენი პაროლი, შეგიძლიათ ყველა მოწყობილობაზე იძულებითი გამოსვლა, რომელიც იყენებს თქვენს ანგარიშს.'; + + @override + String get signOutOtherDevices => 'სხვა მოწყობილობებიდან გამოსვლა'; + + @override + String get doNotSignOut => 'არ მოხდეს გამოსვლა'; + + @override + String get generatingEncryptionKeysTitle => + 'მიმდინარეობს დაშიფრვის გასაღებების გენერირება...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'აღდგენის კოდი'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'მიმდინარეობს გამოსვლა...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => + 'ამოცნობა ვერ მოხერხდა. გთხოვთ, სცადოთ ხელახლა.'; + + @override + String get androidBiometricSuccess => 'წარმატებით'; + + @override + String get androidCancelButton => 'გაუქმება'; + + @override + String get androidSignInTitle => 'საჭიროა აუთენთიფიკაცია'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'სესიის დრო ამოიწურა'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_km.dart b/mobile/packages/strings/lib/l10n/strings_localizations_km.dart new file mode 100644 index 0000000000..e918845c22 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_km.dart @@ -0,0 +1,626 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Khmer Central Khmer (`km`). +class StringsLocalizationsKm extends StringsLocalizations { + StringsLocalizationsKm([String locale = 'km']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'មានកំហុស'; + + @override + String get ok => 'យល់ព្រម'; + + @override + String get faq => 'សំនួរ-ចម្លើយ'; + + @override + String get contactSupport => 'ទំនាក់ទំនងសេវា'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'ចម្លង​អាសយដ្ឋាន​អ៊ីមែល'; + + @override + String get exportLogs => 'នាំចេញកំណត់ហេតុ'; + + @override + String get cancel => 'បោះបង់'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'រក្សាទុក'; + + @override + String get send => 'ផ្ញើរចេញ'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'អ៊ីម៉ែល'; + + @override + String get verify => 'ផ្ទៀងផ្ទាត់'; + + @override + String get invalidEmailTitle => 'អ៊ីម៉ែលនេះមិនត្រឹមត្រូវទេ'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'ផ្ទៀងផ្ទាត់ពាក្យសម្ងាត់'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'សូមសាកល្បងម្តងទៀត'; + + @override + String get enterPassword => 'វាយបញ្ចូល​ពាក្យ​សម្ងាត់'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'សកម្មភាពគណនី'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'មាន​អ្វីមួយ​មិន​ប្រក្រតី។ សូម​ព្យាយាម​ម្តង​ទៀត'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'កាត់់ផ្តាច់សកម្មភាពគណនីនេះ?'; + + @override + String get terminate => 'កាត់ផ្តាច់'; + + @override + String get thisDevice => 'ទូរស័ព្ទនេះ'; + + @override + String get createAccount => 'បង្កើតគណនី'; + + @override + String get weakStrength => 'ខ្សោយ'; + + @override + String get moderateStrength => 'មធ្យម'; + + @override + String get strongStrength => 'ខ្លាំង'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'បង្កើតគណនីថ្មី'; + + @override + String get password => 'ពាក្យសម្ងាត់'; + + @override + String get confirmPassword => 'បញ្ជាក់ពាក្យសម្ងាត់'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'កម្លាំងពាក្យសម្ងាត់: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'គោលការណ៍​ភាព​ឯកជន'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'ចូលគណនី'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'ភ្លេចកូដសម្ងាត់'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'ផ្ញើអ៊ីមែលម្ដងទៀត'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'ពិនិត្យស្ថានភាព'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'ផ្ដាស់ប្ដូរពាក្សសម្ងាត់'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'សង្គ្រោះមកវិញ'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'ភ្លាមៗ'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'លាក់ពត័មាន'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'ប៉ះដើម្បីដោះសោ'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'វាយបញ្ចូល​លេខ​កូដ PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'បញ្ចូលពាក្យសម្ងាត់ម្តងទៀត'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'បោះបង់'; + + @override + String get androidSignInTitle => 'តម្រូវឱ្យមានការបញ្ជាក់ភាពត្រឹមត្រូវ'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'អ៊ីម៉ែលត្រូវបានផ្លាស់ប្តូរទៅ$newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index 62edc10f95..bdb0abf69e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -14,40 +14,40 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Ente에 접속할 수 없습니다, 잠시 후에 다시 시도해주세요. 에러가 반복되는 경우, 저희 지원 팀에 문의해주세요.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + '뭔가 잘못된 것 같습니다. 잠시 후에 다시 시도해주세요. 에러가 반복되는 경우, 저희 지원 팀에 문의해주세요.'; @override - String get error => 'Error'; + String get error => '에러'; @override - String get ok => 'Ok'; + String get ok => '확인'; @override String get faq => 'FAQ'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => '지원 문의'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => '로그를 이메일로 보내기'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return '이 로그를 $toEmail 쪽으로 보내주세요'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => '이메일 주소 복사'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => '로그 내보내기'; @override - String get cancel => 'Cancel'; + String get cancel => '취소'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsKo extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => '버그 제보'; @override String get logsDialogBody => @@ -100,505 +88,485 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return '$endpoint에 접속됨'; } @override - String get save => 'Save'; + String get save => '저장'; @override - String get send => 'Send'; + String get send => '보내기'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + '이것을 당신의 스토리지 (일반적으로 다운로드 폴더) 에 저장하시겠습니까, 아니면 다른 App으로 전송하시겠습니까?'; @override - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + String get saveOnlyDescription => '이것을 당신의 스토리지 (일반적으로 다운로드 폴더) 에 저장하시겠습니까?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => '이메일'; @override - String get verify => 'Verify'; + String get verify => '인증'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => '잘못 된 이메일 주소'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => '유효한 이메일 주소를 입력해주세요'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => '잠시만 기다려주세요...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => '비밀번호 확인'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => '올바르지 않은 비밀번호'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => '다시 시도해주세요'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => '암호 입력'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => '암호 입력'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => '활성화된 Session'; @override - String get oops => 'Oops'; + String get oops => '이런!'; @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + String get somethingWentWrongPleaseTryAgain => '뭔가 잘못됐습니다, 다시 시도해주세요'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => '이 작업을 하시면 기기에서 로그아웃하게 됩니다!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + '이 작업을 하시면 다음 기기에서 로그아웃하게 됩니다:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => '세션을 종결하시겠습니까?'; @override - String get terminate => 'Terminate'; + String get terminate => '종결'; @override - String get thisDevice => 'This device'; + String get thisDevice => '이 기기'; @override - String get createAccount => 'Create account'; + String get createAccount => '계정 만들기'; @override - String get weakStrength => 'Weak'; + String get weakStrength => '약함'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => '보통'; @override - String get strongStrength => 'Strong'; + String get strongStrength => '강함'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => '계정 삭제하기'; @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + String get deleteAccountQuery => '떠나신다니 아쉽습니다. 뭔가 문제가 있으셨나요?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => '네, 피드백을 보냅니다'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => '아니오, 계정을 지웁니다'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => '계정 삭제 절차를 시작하려면 인증 절차를 거쳐주세요'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => '계정 삭제 확인'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + '다른 Ente의 서비스를 이용하고 계시다면, 해당 계정은 모두 연결이 되어있습니다.\n\n모든 Ente 서비스에 업로드 된 당신의 데이터는 삭제 수순에 들어가며, 계정은 불가역적으로 삭제됩니다.'; @override - String get delete => 'Delete'; + String get delete => '삭제'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => '새 계정 만들기'; @override - String get password => 'Password'; + String get password => '암호'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => '암호 확인'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return '암호 보안 강도: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ente에 대해 어떻게 알게 되셨나요? (선택사항)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + '저희는 어플 설치 과정을 관찰하지 않습니다. 어디에서 저희를 발견하셨는지 알려주신다면 도움이 될 겁니다!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + '나는 사용자 약관개인정보 취급방침에 동의합니다.'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => '약관'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => '개인정보 취급 방침'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + '나는 암호를 분실한 경우, 데이터가 종단 간 암호화되어있기에 데이터를 손실할 수 있음을 이해합니다.'; @override - String get encryption => 'Encryption'; + String get encryption => '암호화'; @override - String get logInLabel => 'Log in'; + String get logInLabel => '로그인'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '돌아오신 것을 환영합니다!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + '로그인을 누름으로써, 나는 사용자 약관개인정보 취급방침에 동의합니다.'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => '인터넷 연결 없음'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + '인터넷 연결을 확인하시고 다시 시도해주세요.'; @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + String get verificationFailedPleaseTryAgain => '검증 실패, 다시 시도해주세요'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => '암호 다시 생성'; @override String get recreatePasswordBody => - '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).'; + '현재 사용 중인 기기는 암호를 확인하기에 적합하지 않으나, 모든 기기에서 작동하는 방식으로 비밀번호를 다시 생성할 수 있습니다.\n\n복구 키를 사용하여 로그인하고 암호를 다시 생성해주세요. (원하시면 현재 사용 중인 암호와 같은 암호를 다시 사용하실 수 있습니다.)'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => '복구 키 사용'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => '암호 분실'; @override - String get changeEmail => 'Change email'; + String get changeEmail => '이메일 변경'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => '이메일 인증하기'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '$email 쪽으로 메일을 보냈습니다'; } @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + String get toResetVerifyEmail => '암호를 재설정하시려면, 먼저 이메일을 인증해주세요.'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => '검증을 위해 메일 보관함을 (또는 스팸 메일 보관함) 확인해주세요'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => '눌러서 코드 입력하기'; @override - String get sendEmail => 'Send email'; + String get sendEmail => '이메일 보내기'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => '이메일 다시 보내기'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => '검증 절차가 마무리되지 않았습니다'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => '세션 만료됨'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => '세션이 만료되었습니다. 다시 로그인해주세요.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => '패스키 검증'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => '검증 대기 중...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => '다시 시도해주세요'; @override - String get checkStatus => 'Check status'; + String get checkStatus => '상태 확인'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'TOTP로 로그인 하기'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => '계정 복구'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => '암호 지정'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => '암호 변경'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => '암호 초기화'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => '암호화 키'; @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + String get enterPasswordToEncrypt => '데이터 암호화를 위한 암호 입력'; @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + String get enterNewPasswordToEncrypt => '데이터 암호화를 위한 새로운 암호 입력'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + '저희는 이 암호를 저장하지 않으니, 만약 잊어버리시게 되면, 데이터를 복호화 해드릴 수 없습니다'; @override - String get howItWorks => 'How it works'; + String get howItWorks => '작동 원리'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => '암호화 키 생성 중...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => '암호가 성공적으로 변경되었습니다'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => '다른 기기들에서 로그아웃하기'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + '다른 사람이 내 암호를 알 수도 있을 거란 의심이 드신다면, 당신의 계정을 사용 중인 다른 모든 기기에서 로그아웃할 수 있습니다.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => '다른 기기들을 로그아웃시키기'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => '로그아웃 하지 않기'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => '암호화 키를 생성하는 중...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => '계속'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => '보안이 허술한 기기'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '죄송합니다, 이 기기에서 보안 키를 생성할 수 없습니다.\n\n다른 기기에서 계정을 생성해주세요.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => '클립보드에 복구 키 복사 됨'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => '복구 키'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + '암호를 잊어버린 경우, 데이터를 복구하려면 이 키를 이용하는 방법 뿐입니다.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + '저희는 이 키를 보관하지 않으니, 여기에 있는 24 단어로 구성된 키를 안전하게 보관해주세요.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => '나중에 하기'; @override - String get saveKey => 'Save key'; + String get saveKey => '키 저장하기'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => '다운로드 폴더에 복구 키가 저장되었습니다!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '복구 키가 없으세요?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => '2단계 인증'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => 'Authenticator에 적힌 6 자리 코드를 입력해주세요'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '기기를 잃어버리셨나요?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => '복구 키를 입력하세요'; @override - String get recover => 'Recover'; + String get recover => '복구'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => '로그아웃하는 중...'; @override - String get immediately => 'Immediately'; + String get immediately => '즉시'; @override - String get appLock => 'App lock'; + String get appLock => '어플 잠금'; @override - String get autoLock => 'Auto lock'; + String get autoLock => '자동 잠금'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => '시스템 잠금 찾을 수 없음'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + '기기 잠금을 활성화하시려면, 기기의 암호를 만들거나 시스템 설정에서 화면 잠금을 설정해주세요.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + '기본 잠금 화면이나, PIN 번호나 암호를 사용한 사용자 설정 잠금 화면 중에 선택하세요.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => '기기 잠금'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pin 잠금'; @override - String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + String get autoLockFeatureDescription => 'Background로 App 넘어가고 잠기기까지 걸리는 시간'; @override - String get hideContent => 'Hide content'; + String get hideContent => '내용 숨기기'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'App 전환 화면에서 App의 내용을 숨기고 Screenshot 촬영을 막습니다'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'App 전환 화면에서 App의 내용을 숨깁니다'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => '잘못된 시도 횟수가 너무 많습니다'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => '잠금을 해제하려면 누르세요'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => '로그아웃 하시겠습니까?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => '네, 로그아웃하기'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => '비밀 부분을 확인하려면 인증 절차를 거쳐주세요'; @override - String get next => 'Next'; + String get next => '다음'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => '새 비밀번호 설정'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'PIN 번호 입력'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => '새 PIN 번호 설정'; @override - String get confirm => 'Confirm'; + String get confirm => '확인'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => '암호 다시 입력'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => '핀 다시 입력'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => '신원 확인'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => '식별할 수 없습니다. 다시 시도해주세요.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => '성공'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => '취소'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => '인증 필요'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => '생체인증 필요'; @override - String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + String get androidDeviceCredentialsRequiredTitle => '장치 자격 증명 필요'; @override - String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + String get androidDeviceCredentialsSetupDescription => '장치 자격 증명 필요'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => '설정으로 가기'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + '기기에 생체인증이 설정되어있지 않습니다. \'설정 > 보안\'으로 가셔서 생체인증을 설정해주세요.'; @override - String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + String get iOSLockOut => '생체인증에 문제가 있습니다. 활성화하시려면 기기를 잠궜다가 다시 풀어주세요.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => '확인'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => '이미 등록된 이메일입니다.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => '등록되지 않은 이메일입니다.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => '이 이메일은 이미 사용 중입니다'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return '$newEmail로 메일이 변경되었습니다'; } @override - String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + String get authenticationFailedPleaseTryAgain => '인증절차 실패, 다시 시도해주세요'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => '인증 성공!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => '세션 만료'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => '잘못 된 복구 키'; @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + String get theRecoveryKeyYouEnteredIsIncorrect => '입력하신 복구 키가 맞지 않습니다'; @override - String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + String get twofactorAuthenticationSuccessfullyReset => '2FA가 성공적으로 초기화되었습니다'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +578,27 @@ class StringsLocalizationsKo extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => '검증 코드의 유효시간이 경과하였습니다'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => '잘못된 코드'; @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + String get sorryTheCodeYouveEnteredIsIncorrect => '죄송합니다, 입력하신 코드가 맞지 않습니다'; + + @override + String get developerSettings => '개발자 설정'; + + @override + String get serverEndpoint => '서버 엔드포인트'; + + @override + String get invalidEndpoint => '유효하지 않은 엔드포인트'; + + @override + String get invalidEndpointMessage => + '죄송합니다, 입력하신 엔드포인트가 유효하지 않습니다. 유효한 엔드포인트를 입력하시고 다시 시도해주세요.'; + + @override + String get endpointUpdatedMessage => '엔드포인트가 성공적으로 업데이트됨'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 49e7f4eed4..67514db54f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -10,44 +10,44 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get networkHostLookUpErr => - 'Nepavyksta prisijungti prie \"Ente\". Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi.'; + 'Nepavyksta prisijungti prie „Ente“. Patikrinkite tinklo nustatymus ir susisiekite su palaikymo komanda, jei klaida tęsiasi.'; @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Nepavyksta prisijungti prie „Ente“. Bandykite dar kartą po kurio laiko. Jei klaida tęsiasi, susisiekite su palaikymo komanda.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Atrodo, kad kažkas nutiko ne taip. Bandykite dar kartą po kurio laiko. Jei klaida tęsiasi, susisiekite su mūsų palaikymo komanda.'; @override - String get error => 'Error'; + String get error => 'Klaida'; @override - String get ok => 'Ok'; + String get ok => 'Gerai'; @override - String get faq => 'FAQ'; + String get faq => 'DUK'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Susisiekti su palaikymo komanda'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Atsiųskite žurnalus el. laišku'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Siųskite žurnalus adresu\n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Kopijuoti el. pašto adresą'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Eksportuoti žurnalus'; @override - String get cancel => 'Cancel'; + String get cancel => 'Atšaukti'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsLt extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Pranešti apie riktą'; @override String get logsDialogBody => @@ -100,505 +88,509 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Prijungta prie $endpoint'; } @override - String get save => 'Save'; + String get save => 'Išsaugoti'; @override - String get send => 'Send'; + String get send => 'Siųsti'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Ar norite tai išsaugoti saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke), ar siųsti į kitas programas?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Ar norite tai išsaugoti savo saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'El. paštas'; @override - String get verify => 'Verify'; + String get verify => 'Patvirtinti'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Netinkamas el. pašto adresas'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Įveskite tinkamą el. pašto adresą.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Palaukite...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Patvirtinkite slaptažodį'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Neteisingas slaptažodis.'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Bandykite dar kartą.'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Įveskite slaptažodį'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Įveskite savo slaptažodį'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktyvūs seansai'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Kažkas nutiko ne taip. Bandykite dar kartą.'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Tai jus atjungs nuo šio įrenginio.'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Tai jus atjungs nuo toliau nurodyto įrenginio:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Baigti seansą?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Baigti'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Šis įrenginys'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Kurti paskyrą'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Silpna'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Vidutinė'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stipri'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Ištrinti paskyrą'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Apgailestausime, kad išeinate. Ar susiduriate su kažkokiomis problemomis?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Taip, siųsti atsiliepimą'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Ne, ištrinti paskyrą'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Nustatykite tapatybę, kad pradėtumėte paskyros ištrynimą'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Patvirtinkite paskyros ištrynimą'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Ši paskyra susieta su kitomis „Ente“ programomis, jei jas naudojate.\n\nJūsų įkelti duomenys per visas „Ente“ programas bus planuojama ištrinti, o jūsų paskyra bus ištrinta negrįžtamai.'; @override - String get delete => 'Delete'; + String get delete => 'Ištrinti'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Kurti naują paskyrą'; @override - String get password => 'Password'; + String get password => 'Slaptažodis'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Patvirtinkite slaptažodį'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Slaptažodžio stiprumas: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Kaip išgirdote apie „Ente“? (nebūtina)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote.'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Sutinku su paslaugų sąlygomis ir privatumo politika'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Sąlygos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privatumo politika'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Suprantu, kad jei prarasiu slaptažodį, galiu prarasti savo duomenis, kadangi duomenys yra visapusiškai užšifruota.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifravimas'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Prisijungti'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Sveiki sugrįžę!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Spustelėjus Prisijungti sutinku su paslaugų sąlygomis ir privatumo politika'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Nėra interneto ryšio'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Patikrinkite savo interneto ryšį ir bandykite dar kartą.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Patvirtinimas nepavyko. Bandykite dar kartą.'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Iš naujo sukurti slaptažodį'; @override String get recreatePasswordBody => - '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).'; + 'Dabartinis įrenginys nėra pakankamai galingas, kad patvirtintų jūsų slaptažodį, bet mes galime iš naujo sugeneruoti taip, kad jis veiktų su visais įrenginiais.\n\nPrisijunkite naudodami atkūrimo raktą ir sugeneruokite iš naujo slaptažodį (jei norite, galite vėl naudoti tą patį).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Naudoti atkūrimo raktą'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Pamiršau slaptažodį'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Keisti el. paštą'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Patvirtinti el. paštą'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Išsiuntėme laišką adresu $email.'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Kad iš naujo nustatytumėte slaptažodį, pirmiausia patvirtinkite savo el. paštą.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Patikrinkite savo gautieją (ir šlamštą), kad užbaigtumėte patvirtinimą.'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Palieskite, kad įvestumėte kodą'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Siųsti el. laišką'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Iš naujo siųsti el. laišką'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Vis dar laukiama patvirtinimo'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Seansas baigėsi'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Jūsų seansas baigėsi. Prisijunkite iš naujo.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Slaptarakčio patvirtinimas'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Laukiama patvirtinimo...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Bandyti dar kartą'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Tikrinti būseną'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Prisijungti su TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Atkurti paskyrą'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nustatyti slaptažodį'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Keisti slaptažodį'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Nustatyti slaptažodį iš naujo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifravimo raktai'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Įveskite slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Įveskite naują slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Šio slaptažodžio nesaugome, todėl jei jį pamiršite, negalėsime iššifruoti jūsų duomenų'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Kaip tai veikia'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generuojami šifravimo raktai...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Slaptažodis sėkmingai pakeistas'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Atsijungti iš kitų įrenginių'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jei manote, kad kas nors gali žinoti jūsų slaptažodį, galite priverstinai atsijungti iš visų kitų įrenginių, naudojančių jūsų paskyrą.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Atsijungti kitus įrenginius'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neatsijungti'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generuojami šifravimo raktai...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Tęsti'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Nesaugus įrenginys'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Atsiprašome, šiame įrenginyje nepavyko sugeneruoti saugių raktų.\n\nRegistruokitės iš kito įrenginio.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Nukopijuotas atkūrimo raktas į iškarpinę'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Atkūrimo raktas'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jei pamiršote slaptažodį, vienintelis būdas atkurti duomenis – naudoti šį raktą.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Šio rakto nesaugome, todėl išsaugokite šį 24 žodžių raktą saugioje vietoje.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Daryti tai vėliau'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Išsaugoti raktą'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Atkūrimo raktas išsaugotas atsisiuntimų aplanke.'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Neturite atkūrimo rakto?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvigubas tapatybės nustatymas'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Įveskite 6 skaitmenų kodą\niš autentifikatoriaus programos'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Prarastas įrenginys?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Įveskite atkūrimo raktą'; @override - String get recover => 'Recover'; + String get recover => 'Atkurti'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Atsijungiama...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Iš karto'; @override - String get appLock => 'App lock'; + String get appLock => 'Programos užraktas'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatinis užraktas'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Nerastas sistemos užraktas'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Kad įjungtumėte įrenginio užraktą, sistemos nustatymuose nustatykite įrenginio prieigos kodą arba ekrano užraktą.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Pasirinkite tarp numatytojo įrenginio užrakinimo ekrano ir pasirinktinio užrakinimo ekrano su PIN kodu arba slaptažodžiu.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Įrenginio užraktas'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'PIN užraktas'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Laikas, po kurio programa užrakinama perkėlus ją į foną.'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Slėpti turinį'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Paslepia programų turinį programų perjungiklyje ir išjungia ekrano kopijas.'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Paslepia programos turinį programos perjungiklyje.'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Per daug neteisingų bandymų.'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Palieskite, kad atrakintumėte'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Ar tikrai norite atsijungti?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Taip, atsijungti'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Nustatykite tapatybę, kad peržiūrėtumėte savo paslaptis'; @override - String get next => 'Next'; + String get next => 'Toliau'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Nustatykite naują slaptažodį'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Įveskite PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Nustatykite naują PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Patvirtinti'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Įveskite slaptažodį iš naujo'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Įveskite PIN iš naujo'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Patvirtinkite tapatybę'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Neatpažinta. Bandykite dar kartą.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Sėkmė'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Atšaukti'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Privalomas tapatybės nustatymas'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Privaloma biometrija'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Privalomi įrenginio kredencialai'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Privalomi įrenginio kredencialai'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Eiti į nustatymus'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Biometrinis tapatybės nustatymas jūsų įrenginyje nenustatytas. Eikite į Nustatymai > Saugumas ir pridėkite biometrinį tapatybės nustatymą.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biometrinis tapatybės nustatymas išjungtas. Kad jį įjungtumėte, užrakinkite ir atrakinkite ekraną.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Gerai'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'El. paštas jau užregistruotas.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'El. paštas neregistruotas.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Šis el. paštas jau naudojamas.'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'El. paštas pakeistas į $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Tapatybės nustatymas nepavyko. Bandykite dar kartą.'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Tapatybės nustatymas sėkmingas.'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Seansas baigėsi'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Neteisingas atkūrimo raktas'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Įvestas atkūrimo raktas yra neteisingas.'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Dvigubas tapatybės nustatymas sėkmingai iš naujo nustatytas.'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +603,28 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Jūsų patvirtinimo kodas nebegaliojantis.'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Neteisingas kodas'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Atsiprašome, įvestas kodas yra neteisingas.'; + + @override + String get developerSettings => 'Kūrėjo nustatymai'; + + @override + String get serverEndpoint => 'Serverio galutinis taškas'; + + @override + String get invalidEndpoint => 'Netinkamas galutinis taškas'; + + @override + String get invalidEndpointMessage => + 'Atsiprašome, įvestas galutinis taškas netinkamas. Įveskite tinkamą galutinį tašką ir bandykite dar kartą.'; + + @override + String get endpointUpdatedMessage => 'Galutinis taškas sėkmingai atnaujintas'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lv.dart new file mode 100644 index 0000000000..d6762e17e9 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lv.dart @@ -0,0 +1,626 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Latvian (`lv`). +class StringsLocalizationsLv extends StringsLocalizations { + StringsLocalizationsLv([String locale = 'lv']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Kļūda'; + + @override + String get ok => 'Labi'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Atcelt'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Saglabāt'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'E-pasts'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Nederīga e-pasta adrese'; + + @override + String get invalidEmailMessage => 'Lūdzu ievadiet derīgu e-pasta adresi.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Ievadiet paroli'; + + @override + String get enterYourPasswordHint => 'Ievadiet savu paroli'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'Šī ierīce'; + + @override + String get createAccount => 'Izveidot kontu'; + + @override + String get weakStrength => 'Vāja'; + + @override + String get moderateStrength => 'Vidēji spēcīga'; + + @override + String get strongStrength => 'Spēcīga'; + + @override + String get deleteAccount => 'Dzēst kontu'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'Nē, dzēst kontu'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Apstiprināt konta dzēšanu'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Dzēst'; + + @override + String get createNewAccount => 'Izveidot jaunu kontu'; + + @override + String get password => 'Parole'; + + @override + String get confirmPassword => 'Apstiprināt paroli'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Šifrēšana'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'Nav interneta savienojums'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'Kā tas darbojas'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Saglabāt atslēgu'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Slēpt saturu'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Atcelt'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'Šis e-pasts jau tiek izmantots'; + + @override + String emailChangedTo(String newEmail) { + return 'E-pasts nomainīts uz $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ml.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ml.dart new file mode 100644 index 0000000000..cf523a3fb0 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ml.dart @@ -0,0 +1,627 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Malayalam (`ml`). +class StringsLocalizationsMl extends StringsLocalizations { + StringsLocalizationsMl([String locale = 'ml']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'ശരി'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'Contact support'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'റദ്ദാക്കുക'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Report a bug'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'ഇമെയിൽ'; + + @override + String get verify => 'പരിശോധിക്കുക'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'പാസ്‌വേഡ് സ്ഥിരീകരിക്കുക'; + + @override + String get incorrectPasswordTitle => 'തെറ്റായ പാസ്‌വേഡ്'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'പുതിയ അക്കൗണ്ട് സൃഷ്ടിക്കുക'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'പാസ്വേഡ് സ്ഥിരീകരിക്കുക'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'വീണ്ടും സ്വാഗതം!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'ഇമെയിൽ മാറ്റുക'; + + @override + String get verifyEmail => 'ഇമെയിൽ സ്ഥിരീകരിക്കുക'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'ടു-ഫാക്ടർ ആധികാരികത'; + + @override + String get enterCodeHint => + 'നിങ്ങളുടെ ഓതന്റിക്കേറ്റർ ആപ്പിൽ നിന്നുള്ള 6 അക്ക കോഡ് നൽകുക'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => + 'ഇമെയിൽ ഇതിനകം രജിസ്റ്റർ ചെയ്തിട്ടുണ്ട്.'; + + @override + String get emailNotRegistered => 'ഇമെയിൽ രജിസ്റ്റർ ചെയ്തിട്ടില്ല.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index fb44290723..7fb99652a5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -14,40 +14,40 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Kan geen verbinding maken met Ente, probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met support.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Het lijkt erop dat er iets fout is gegaan. Probeer het later opnieuw. Als de fout zich blijft voordoen, neem dan contact op met ons supportteam.'; @override - String get error => 'Error'; + String get error => 'Foutmelding'; @override - String get ok => 'Ok'; + String get ok => 'Oké'; @override - String get faq => 'FAQ'; + String get faq => 'Veelgestelde vragen'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Klantenservice'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'E-mail uw logs'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Verstuur de logs alsjeblieft naar $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'E-mailadres kopiëren'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Logs exporteren'; @override - String get cancel => 'Cancel'; + String get cancel => 'Annuleer'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsNl extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Een fout melden'; @override String get logsDialogBody => @@ -100,505 +88,512 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Verbonden met $endpoint'; } @override - String get save => 'Save'; + String get save => 'Opslaan'; @override - String get send => 'Send'; + String get send => 'Verzenden'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Wil je dit opslaan naar je opslagruimte (Downloads map) of naar andere apps versturen?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Wil je dit opslaan naar je opslagruimte (Downloads map)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Voer uw nieuwe e-mailadres in'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Verifiëren'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ongeldig e-mailadres'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Voer een geldig e-mailadres in.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Een ogenblik geduld...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bevestig wachtwoord'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Onjuist wachtwoord'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Probeer het nog eens'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Voer wachtwoord in'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Voer je wachtwoord in'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Actieve sessies'; @override - String get oops => 'Oops'; + String get oops => 'Oeps'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Er is iets fout gegaan, probeer het opnieuw'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Dit zal je uitloggen van dit apparaat!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Dit zal je uitloggen van het volgende apparaat:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Sessie beëindigen?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Beëindigen'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Dit apparaat'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Account aanmaken'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Zwak'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Matig'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Sterk'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Account verwijderen'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'We zullen het vervelend vinden om je te zien vertrekken. Zijn er problemen?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, geef feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nee, verwijder account'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Gelieve te verifiëren om het account te verwijderen'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Account verwijderen bevestigen'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Dit account is gekoppeld aan andere Ente apps, als je er gebruik van maakt.\n\nJe geüploade gegevens worden in alle Ente apps gepland voor verwijdering, en je account wordt permanent verwijderd voor alle Ente diensten.'; @override - String get delete => 'Delete'; + String get delete => 'Verwijderen'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Nieuw account aanmaken'; @override - String get password => 'Password'; + String get password => 'Wachtwoord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Wachtwoord bevestigen'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Wachtwoord sterkte: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hoe hoorde je over Ente? (optioneel)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Wij gebruiken geen tracking. Het zou helpen als je ons vertelt waar je ons gevonden hebt!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Voorwaarden'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privacybeleid'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Ik begrijp dat als ik mijn wachtwoord verlies, ik mijn gegevens kan verliezen omdat mijn gegevens end-to-end versleuteld zijn.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Encryptie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Inloggen'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Welkom terug!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Door op inloggen te klikken, ga ik akkoord met de gebruiksvoorwaarden en privacybeleid'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Geen internetverbinding'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Controleer je internetverbinding en probeer het opnieuw.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verificatie mislukt, probeer het opnieuw'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Wachtwoord opnieuw instellen'; @override String get recreatePasswordBody => - '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).'; + '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 herstelsleutel en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Herstelsleutel gebruiken'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Wachtwoord vergeten'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'E-mailadres wijzigen'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Bevestig e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'We hebben een e-mail gestuurd naar $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Verifieer eerst je e-mailadres om je wachtwoord opnieuw in te stellen.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Controleer je inbox (en spam) om verificatie te voltooien'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tik om code in te voeren'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'E-mail versturen'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'E-mail opnieuw versturen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verificatie is nog in behandeling'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessie verlopen'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Jouw sessie is verlopen. Log opnieuw in.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Passkey verificatie'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Wachten op verificatie...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Probeer opnieuw'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Status controleren'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Inloggen met TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Account herstellen'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Wachtwoord instellen'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Wachtwoord wijzigen'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Wachtwoord resetten'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Encryptiesleutels'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Voer een wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Voer een nieuw wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Wij slaan dit wachtwoord niet op, dus als je het vergeet, kunnen we jouw gegevens niet ontsleutelen'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Hoe het werkt'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Encryptiesleutels worden gegenereerd...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Wachtwoord succesvol aangepast'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Afmelden bij andere apparaten'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Als je denkt dat iemand je wachtwoord zou kunnen kennen, kun je alle andere apparaten die je account gebruiken dwingen om uit te loggen.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Afmelden bij andere apparaten'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Niet uitloggen'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Encryptiesleutels genereren...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Doorgaan'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Onveilig apparaat'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Sorry, we konden geen beveiligde sleutels genereren op dit apparaat.\n\nMeld je aan vanaf een ander apparaat.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Herstelsleutel gekopieerd naar klembord'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Herstelsleutel'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Als je je wachtwoord vergeet, kun je alleen met deze code je gegevens herstellen.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'We slaan deze code niet op, bewaar deze code met 24 woorden op een veilige plaats.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Doe dit later'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Sleutel opslaan'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Herstelsleutel opgeslagen in de Downloads map!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Geen herstelsleutel?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tweestapsverificatie'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Voer de 6-cijferige code van je verificatie-app in'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Apparaat verloren?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Voer je herstelsleutel in'; @override - String get recover => 'Recover'; + String get recover => 'Herstellen'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Bezig met uitloggen...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Onmiddellijk'; @override - String get appLock => 'App lock'; + String get appLock => 'App-vergrendeling'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatische vergrendeling'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Geen systeemvergrendeling gevonden'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Om toestelvergrendeling in te schakelen, stelt u de toegangscode van het apparaat of schermvergrendeling in uw systeeminstellingen in.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Kies tussen de standaard schermvergrendeling van uw apparaat en een aangepaste schermvergrendeling met een pincode of wachtwoord.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Apparaat vergrendeling'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pin vergrendeling'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Tijd waarna de app vergrendelt nadat ze op de achtergrond is gezet'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Inhoud verbergen'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Verbergt de app inhoud in de app switcher en schakelt schermafbeeldingen uit'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Verbergt de inhoud van de app in de app switcher'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Te veel onjuiste pogingen'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Tik om te ontgrendelen'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Weet je zeker dat je wilt uitloggen?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ja, uitloggen'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Graag verifiëren om uw herstelsleutel te bekijken'; @override - String get next => 'Next'; + String get next => 'Volgende'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Nieuw wachtwoord instellen'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Pin invoeren'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Nieuwe pin instellen'; @override - String get confirm => 'Confirm'; + String get confirm => 'Bevestig'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Wachtwoord opnieuw invoeren'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'PIN opnieuw invoeren'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Identiteit verifiëren'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Niet herkend. Probeer het opnieuw.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Succes'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Annuleren'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Verificatie vereist'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => + 'Biometrische verificatie vereist'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Apparaatgegevens vereist'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Apparaatgegevens vereist'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Ga naar instellingen'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Biometrische verificatie is niet ingesteld op uw apparaat. Ga naar \'Instellingen > Beveiliging\' om biometrische verificatie toe te voegen.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biometrische verificatie is uitgeschakeld. Vergrendel en ontgrendel uw scherm om het in te schakelen.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Oké'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-mail is al geregistreerd.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-mail niet geregistreerd.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Dit e-mailadres is al in gebruik'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'E-mailadres gewijzigd naar $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Verificatie mislukt, probeer het opnieuw'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Verificatie geslaagd!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Sessie verlopen'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Onjuiste herstelsleutel'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'De ingevoerde herstelsleutel is onjuist'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Tweestapsverificatie succesvol gereset'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +605,28 @@ class StringsLocalizationsNl extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => 'Uw verificatiecode is verlopen'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Onjuiste code'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Sorry, de ingevoerde code is onjuist'; + + @override + String get developerSettings => 'Ontwikkelaarsinstellingen'; + + @override + String get serverEndpoint => 'Server eindpunt'; + + @override + String get invalidEndpoint => 'Ongeldig eindpunt'; + + @override + String get invalidEndpointMessage => + 'Sorry, het eindpunt dat u hebt ingevoerd is ongeldig. Voer een geldig eindpunt in en probeer het opnieuw.'; + + @override + String get endpointUpdatedMessage => 'Eindpunt met succes bijgewerkt'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index 6e5029db99..5d636efe39 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -14,40 +14,40 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Nie można połączyć się z Ente, spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z pomocą techniczną.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Wygląda na to, że coś poszło nie tak. Spróbuj ponownie po pewnym czasie. Jeśli błąd będzie się powtarzał, skontaktuj się z naszym zespołem pomocy technicznej.'; @override - String get error => 'Error'; + String get error => 'Błąd'; @override String get ok => 'Ok'; @override - String get faq => 'FAQ'; + String get faq => 'Najczęściej zadawane pytania (FAQ)'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Skontaktuj się z pomocą techniczną'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Wyślij mailem logi'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Prosimy wysłać logi do $toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Kopiuj adres e-mail'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Eksportuj logi'; @override - String get cancel => 'Cancel'; + String get cancel => 'Anuluj'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsPl extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Zgłoś błąd'; @override String get logsDialogBody => @@ -100,505 +88,510 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Połączono z $endpoint'; } @override - String get save => 'Save'; + String get save => 'Zapisz'; @override - String get send => 'Send'; + String get send => 'Wyślij'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane) czy wysłać to do innych aplikacji?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Wprowadź nowy adres e-mail'; @override String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Zweryfikuj'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Nieprawidłowy adres e-mail'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Prosimy podać prawidłowy adres e-mail.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Prosimy czekać...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Zweryfikuj hasło'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nieprawidłowe hasło'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Prosimy spróbować ponownie'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Wprowadź hasło'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Wprowadź swoje hasło'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktywne sesje'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Coś poszło nie tak, spróbuj ponownie'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'To wyloguje Cię z tego urządzenia!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'To wyloguje Cię z tego urządzenia:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Zakończyć sesję?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Zakończ'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'To urządzenie'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Utwórz konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Słabe'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Umiarkowane'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silne'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Usuń konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Będzie nam przykro, że odchodzisz. Masz jakiś problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Tak, wyślij opinię'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nie, usuń moje konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Prosimy uwierzytelnić się, aby zainicjować usuwanie konta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potwierdź usunięcie konta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'To konto jest połączone z innymi aplikacjami Ente, jeśli ich używasz.\n\nTwoje przesłane dane, we wszystkich aplikacjach Ente, zostaną zaplanowane do usunięcia, a Twoje konto zostanie trwale usunięte.'; @override - String get delete => 'Delete'; + String get delete => 'Usuń'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Utwórz nowe konto'; @override - String get password => 'Password'; + String get password => 'Hasło'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potwierdź hasło'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Siła hasła: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Jak usłyszałeś/aś o Ente? (opcjonalnie)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Akceptuję warunki korzystania z usługi i politykę prywatności'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Regulamin'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Polityka prywatności'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumiem, że jeśli utracę hasło, mogę stracić moje dane, ponieważ moje dane są szyfrowane end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Szyfrowanie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Zaloguj się'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Witaj ponownie!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Klikając, zaloguj się, zgadzam się na regulamin i politykę prywatności'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Brak połączenia z Internetem'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Prosimy sprawdzić połączenie internetowe i spróbować ponownie.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Weryfikacja nie powiodła się, spróbuj ponownie'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Zresetuj hasło'; @override String get recreatePasswordBody => - '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).'; + 'Obecne urządzenie nie jest wystarczająco wydajne, aby zweryfikować Twoje hasło, więc musimy je raz zregenerować w sposób, który działa ze wszystkimi urządzeniami. \n\nZaloguj się przy użyciu klucza odzyskiwania i zresetuj swoje hasło (możesz ponownie użyć tego samego, jeśli chcesz).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Użyj kodu odzyskiwania'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Nie pamiętam hasła'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Zmień adres e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Zweryfikuj adres e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Wysłaliśmy wiadomość do $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Aby zresetować hasło, najpierw zweryfikuj swój e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Dotknij, aby wprowadzić kod'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Wyślij e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Wyślij e-mail ponownie'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Weryfikacja jest nadal w toku'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sesja wygasła'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Twoja sesja wygasła. Zaloguj się ponownie.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Weryfikacja kluczem dostępu'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Oczekiwanie na weryfikację...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Spróbuj ponownie'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Sprawdź stan'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Zaloguj się za pomocą TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Odzyskaj konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ustaw hasło'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Zmień hasło'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Zresetuj hasło'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Klucz szyfrowania'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Wprowadź hasło, którego możemy użyć do zaszyfrowania Twoich danych'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Wprowadź nowe hasło, którego możemy użyć do zaszyfrowania Twoich danych'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Nie przechowujemy tego hasła, więc jeśli go zapomnisz, nie będziemy w stanie odszyfrować Twoich danych'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Jak to działa'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generowanie kluczy szyfrujących...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Hasło zostało pomyślnie zmienione'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Wyloguj z pozostałych urządzeń'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jeśli uważasz, że ktoś może znać Twoje hasło, możesz wymusić wylogowanie na wszystkich innych urządzeniach korzystających z Twojego konta.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Wyloguj z pozostałych urządzeń'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Nie wylogowuj mnie'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Generowanie kluczy szyfrujących...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Kontynuuj'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Niezabezpieczone urządzenie'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Przepraszamy, nie mogliśmy wygenerować kluczy bezpiecznych na tym urządzeniu.\n\nZarejestruj się z innego urządzenia.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Klucz odzyskiwania został skopiowany do schowka'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Klucz odzyskiwania'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jeśli zapomnisz hasła, jedynym sposobem na odzyskanie danych jest ten klucz.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nie przechowujemy tego klucza, prosimy zachować ten 24-słowny klucz w bezpiecznym miejscu.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Zrób to później'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Zapisz klucz'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Klucz odzyskiwania zapisany w folderze Pobrane!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Brak klucza odzyskiwania?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Uwierzytelnianie dwustopniowe'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Wprowadź sześciocyfrowy kod z \nTwojej aplikacji uwierzytelniającej'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Zagubiono urządzenie?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Wprowadź swój klucz odzyskiwania'; @override - String get recover => 'Recover'; + String get recover => 'Odzyskaj'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Wylogowywanie...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Natychmiast'; @override - String get appLock => 'App lock'; + String get appLock => 'Blokada aplikacji'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatyczna blokada'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Nie znaleziono blokady systemowej'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Aby włączyć blokadę aplikacji, należy skonfigurować hasło urządzenia lub blokadę ekranu w ustawieniach Twojego systemu.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Wybierz między domyślnym ekranem blokady urządzenia a niestandardowym ekranem blokady z kodem PIN lub hasłem.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Blokada urządzenia'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Blokada PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Czas, po którym aplikacja blokuje się po umieszczeniu jej w tle'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Ukryj zawartość'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Ukrywa zawartość aplikacji w przełączniku aplikacji i wyłącza zrzuty ekranu'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Ukrywa zawartość aplikacji w przełączniku aplikacji'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Zbyt wiele błędnych prób'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Naciśnij, aby odblokować'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Czy na pewno chcesz się wylogować?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Tak, wyloguj'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Prosimy uwierzytelnić się, aby wyświetlić swoje sekrety'; @override - String get next => 'Next'; + String get next => 'Dalej'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Ustaw nowe hasło'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Wprowadź kod PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Ustaw nowy kod PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Potwierdź'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Wprowadź ponownie hasło'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Wprowadź ponownie kod PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Potwierdź swoją tożsamość'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Nie rozpoznano. Spróbuj ponownie.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Sukces'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Anuluj'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Wymagana autoryzacja'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Wymagana biometria'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Wymagane dane logowania urządzenia'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Wymagane dane logowania urządzenia'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Przejdź do ustawień'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Uwierzytelnianie biometryczne nie jest skonfigurowane na tym urządzeniu. Przejdź do \'Ustawienia > Bezpieczeństwo\', aby dodać uwierzytelnianie biometryczne.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Uwierzytelnianie biometryczne jest wyłączone. Prosimy zablokować i odblokować ekran, aby je włączyć.'; @override String get iOSOkButton => 'OK'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Adres e-mail jest już zarejestrowany.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Adres e-mail nie jest zarejestrowany.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Ten adres e-mail już jest zajęty'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Adres e-mail został zmieniony na $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Uwierzytelnianie nie powiodło się, prosimy spróbować ponownie'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Uwierzytelnianie powiodło się!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Sesja wygasła'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Nieprawidłowy klucz odzyskiwania'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Wprowadzony klucz odzyskiwania jest nieprawidłowy'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Pomyślnie zresetowano uwierzytelnianie dwustopniowe'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +603,28 @@ class StringsLocalizationsPl extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => 'Twój kod weryfikacyjny wygasł'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Nieprawidłowy kod'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Niestety, wprowadzony kod jest nieprawidłowy'; + + @override + String get developerSettings => 'Ustawienia dla programistów'; + + @override + String get serverEndpoint => 'Punkt końcowy serwera'; + + @override + String get invalidEndpoint => 'Punkt końcowy jest nieprawidłowy'; + + @override + String get invalidEndpointMessage => + 'Niestety, wprowadzony punkt końcowy jest nieprawidłowy. Wprowadź prawidłowy punkt końcowy i spróbuj ponownie.'; + + @override + String get endpointUpdatedMessage => 'Punkt końcowy zaktualizowany pomyślnie'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index 0fec72c8f5..b339c4f481 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -14,40 +14,40 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Não foi possível conectar ao Ente, tente novamente após algum tempo. Se o erro persistir, entre em contato com o suporte.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Parece que algo deu errado. Tente novamente mais tarde. Se o erro persistir, entre em contato com nossa equipe de ajuda.'; @override - String get error => 'Error'; + String get error => 'Erro'; @override String get ok => 'Ok'; @override - String get faq => 'FAQ'; + String get faq => 'Perguntas frequentes'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Contatar suporte'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Enviar registros por e-mail'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Envie os logs para \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Copiar endereço de e-mail'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exportar logs'; @override - String get cancel => 'Cancel'; + String get cancel => 'Cancelar'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsPt extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Informar erro'; @override String get logsDialogBody => @@ -100,505 +88,508 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Conectado a $endpoint'; } @override - String get save => 'Save'; + String get save => 'Salvar'; @override - String get send => 'Send'; + String get send => 'Enviar'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Deseja mesmo salvar isso no armazenamento (pasta de Downloads por padrão) ou enviar a outros aplicativos?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Deseja mesmo salvar em seu armazenamento (pasta de Downloads por padrão)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Insira seu novo e-mail'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Verificar'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Endereço de e-mail inválido'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Insira um endereço de e-mail válido.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Aguarde...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verificar senha'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Senha incorreta'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Tente novamente'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Inserir senha'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Insira sua senha'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sessões ativas'; @override - String get oops => 'Oops'; + String get oops => 'Opa'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Algo deu errado. Tente outra vez'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Isso fará com que você saia deste dispositivo!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Isso fará você sair do dispositivo a seguir:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Sair?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Encerrar'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Esse dispositivo'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Criar conta'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Fraca'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Moderada'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Forte'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Excluir conta'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Estamos tristes por vê-lo sair. Você enfrentou algum problema?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Sim, enviar feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Não, excluir conta'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Autentique-se para iniciar a exclusão de conta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmar exclusão de conta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Esta conta está vinculada a outros apps Ente, se você usa algum.\n\nSeus dados enviados, entre todos os apps Ente, serão marcados para exclusão, e sua conta será apagada permanentemente.'; @override - String get delete => 'Delete'; + String get delete => 'Excluir'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Criar nova conta'; @override - String get password => 'Password'; + String get password => 'Senha'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmar senha'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Força da senha: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Como você descobriu o Ente? (opcional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Não rastreamos instalações. Ajudaria bastante se você contasse onde nos achou!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Eu concordo com os termos de serviço e a política de privacidade'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Termos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Política de Privacidade'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Eu entendo que se eu perder minha senha, posso perder meus dados, já que meus dados são criptografados de ponta a ponta.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Criptografia'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Entrar'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Bem-vindo(a) de volta!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Ao clicar em iniciar sessão, eu concordo com os termos de serviço e a política de privacidade'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Não conectado à internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Verifique sua conexão com a internet e tente novamente.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Falhou na verificação. Tente novamente'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Redefinir senha'; @override String get recreatePasswordBody => - '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).'; + 'Não é possível verificar a sua senha no dispositivo atual, mas podemos regenerá-la para que funcione em todos os dispositivos. \n\nEntre com a sua chave de recuperação e regenere sua senha (você pode usar a mesma se quiser).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Usar chave de recuperação'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Esqueci a senha'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Alterar e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verificar e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Enviamos um e-mail à $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Para redefinir sua senha, verifique seu e-mail primeiramente.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Verifique sua caixa de entrada (e spam) para concluir a verificação'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Toque para inserir código'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Enviar e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Reenviar e-mail'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'A verificação ainda está pendente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessão expirada'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Sua sessão expirou. Registre-se novamente.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verificação de chave de acesso'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Aguardando verificação...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Tente novamente'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Verificar status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Registrar com TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Recuperar conta'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Definir senha'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Alterar senha'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Redefinir senha'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Chaves de criptografia'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Insira uma senha que podemos usar para criptografar seus dados'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Insira uma nova senha para criptografar seus dados'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Não salvamos esta senha, então se você esquecê-la, não podemos descriptografar seus dados'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Como funciona'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Gerando chaves de criptografia...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'A senha foi alterada'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Sair da conta em outros dispositivos'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Se você acha que alguém possa saber da sua senha, você pode forçar desconectar sua conta de outros dispositivos.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Sair em outros dispositivos'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Não sair'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Gerando chaves de criptografia...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuar'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Dispositivo inseguro'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Desculpe, não foi possível gerar chaves de segurança nesse dispositivo.\n\ninicie sessão em um dispositivo diferente.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Chave de recuperação copiada para a área de transferência'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Chave de recuperação'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Caso esqueça sua senha, a única maneira de recuperar seus dados é com esta chave.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Não armazenamos esta chave de 24 palavras. Salve-a em um lugar seguro.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Fazer isso depois'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Salvar chave'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Chave de recuperação salva na pasta Downloads!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Sem chave de recuperação?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autenticação de dois fatores'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Insira o código de 6 dígitos do aplicativo autenticador'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Perdeu o dispositivo?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Digite a chave de recuperação'; @override - String get recover => 'Recover'; + String get recover => 'Recuperar'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Desconectando...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Imediatamente'; @override - String get appLock => 'App lock'; + String get appLock => 'Bloqueio do aplicativo'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Bloqueio automático'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Nenhum bloqueio do sistema encontrado'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Para ativar o bloqueio do dispositivo, configure a senha do dispositivo ou o bloqueio de tela nas configurações do seu sistema.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Escolha entre a tela de bloqueio padrão do seu dispositivo e uma tela de bloqueio personalizada com PIN ou senha.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Bloqueio do dispositivo'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'PIN de bloqueio'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Tempo de bloqueio do aplicativo em segundo plano'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Ocultar conteúdo'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Oculta o conteúdo do aplicativo no seletor de aplicativos e desativa as capturas de tela'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Oculta o conteúdo do seletor de aplicativos'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Muitas tentativas incorretas'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Toque para desbloquear'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Deseja mesmo sair?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Sim, quero sair'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => 'Autentique-se para ver suas chaves secretas'; @override - String get next => 'Next'; + String get next => 'Avançar'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Defina a nova senha'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Inserir PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Definir novo PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Confirmar'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Reinserir senha'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Reinserir PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Verificar identidade'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Não reconhecido. Tente de novo.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Sucesso'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Cancelar'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Autenticação necessária'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Biometria necessária'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Credenciais necessários do dispositivo'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Credenciais necessários do dispositivo'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Ir para Opções'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'A autenticação biométrica não está configurada no seu dispositivo. Vá em \'Configurações > Segurança\' para adicionar a autenticação biométrica.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'A autenticação biométrica está desativada. Bloqueie e desbloqueie sua tela para ativá-la.'; @override String get iOSOkButton => 'OK'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-mail já registrado.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-mail não registrado.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Este e-mail já está em uso'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'E-mail alterado para $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'A autenticação falhou. Tente novamente'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Autenticado!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Sessão expirada'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Chave de recuperação incorreta'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'A chave de recuperação inserida está incorreta'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Autenticação de dois fatores redefinida com sucesso'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +602,28 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Seu código de verificação expirou'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Código incorreto'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'O código inserido está incorreto'; + + @override + String get developerSettings => 'Opções de Desenvolvedor'; + + @override + String get serverEndpoint => 'Endpoint do servidor'; + + @override + String get invalidEndpoint => 'Endpoint inválido'; + + @override + String get invalidEndpointMessage => + 'Desculpe, o ponto de acesso inserido é inválido. Insira um ponto de acesso válido e tente novamente.'; + + @override + String get endpointUpdatedMessage => 'O endpoint foi atualizado'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ro.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ro.dart new file mode 100644 index 0000000000..432e033823 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ro.dart @@ -0,0 +1,631 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Romanian Moldavian Moldovan (`ro`). +class StringsLocalizationsRo extends StringsLocalizations { + StringsLocalizationsRo([String locale = 'ro']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Eroare'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'Întrebări frecvente'; + + @override + String get contactSupport => 'Contactează suportul'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Te rugăm să trimiți jurnalele la $toEmail'; + } + + @override + String get copyEmailAddress => 'Copiază adresa de e-mail'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Anulare'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Raportează o eroare'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Salvare'; + + @override + String get send => 'Trimitere'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'E-mail'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Adresa e-mail nu este validă'; + + @override + String get invalidEmailMessage => + 'Te rugăm să introduci o adresă de e-mail validă.'; + + @override + String get pleaseWait => 'Te rog așteaptă...'; + + @override + String get verifyPassword => 'Verifică parola'; + + @override + String get incorrectPasswordTitle => 'Parolă incorectă'; + + @override + String get pleaseTryAgain => 'Te rugăm să încerci din nou'; + + @override + String get enterPassword => 'Introdu parola'; + + @override + String get enterYourPasswordHint => 'Introdu parola'; + + @override + String get activeSessions => 'Sesiuni active'; + + @override + String get oops => 'Ups'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Ceva n-a mers bine, te rog încearcă din nou'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'Acest dispozitiv'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Ștergere cont'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Da, trimite feedback'; + + @override + String get noDeleteAccountAction => 'Nu, șterge contul'; + + @override + String get initiateAccountDeleteTitle => + 'Te rugăm să te autentifici pentru a iniția ștergerea contului'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Ștergere'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Parolă'; + + @override + String get confirmPassword => 'Confirmă parola'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Termeni'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Bine ai revenit!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'Nu există conexiune la internet'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreează parola'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Am uitat parola'; + + @override + String get changeEmail => 'Schimbă e-mailul'; + + @override + String get verifyEmail => 'Verifică e-mail'; + + @override + String weHaveSendEmailTo(String email) { + return 'Am trimis un e-mail la $email'; + } + + @override + String get toResetVerifyEmail => + 'Pentru a reseta parola, te rugăm să confirmi mai întâi adresa de e-mail.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Apasă pentru a introduce codul'; + + @override + String get sendEmail => 'Trimite e-mail'; + + @override + String get resendEmail => 'Retrimite e-mail'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Sesiune expirată'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Încearcă din nou'; + + @override + String get checkStatus => 'Verifică status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recuperare cont'; + + @override + String get setPasswordTitle => 'Setează parola'; + + @override + String get changePasswordTitle => 'Schimbă parola'; + + @override + String get resetPasswordTitle => 'Resetează parola'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Introdu o parolă pe care o putem folosi pentru a-ți cripta datele'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'Nu stocăm această parolă, deci dacă o uiți, nu îți putem decripta datele'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => + 'Parola a fost modificată cu succes'; + + @override + String get signOutFromOtherDevices => 'Deconectare de pe alte dispozitive'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Deconectează alte dispozitive'; + + @override + String get doNotSignOut => 'Nu te deconecta'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Cheie de recuperare salvată în clipboard'; + + @override + String get recoveryKey => 'Cheie de recuperare'; + + @override + String get recoveryKeyOnForgotPassword => + 'Dacă îți uiți parola, singura modalitate prin care poți recupera datele este cu această cheie.'; + + @override + String get recoveryKeySaveDescription => + 'Nu stocăm această cheie, vă rugăm salvați această cheie de 24 de cuvinte într-un loc sigur.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Salvare cheie'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Autentificare cu doi factori'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recuperează'; + + @override + String get loggingOut => 'Deconectare...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verifică identitatea'; + + @override + String get androidBiometricNotRecognized => + 'Neidentificat. Încearcă din nou.'; + + @override + String get androidBiometricSuccess => 'Succes'; + + @override + String get androidCancelButton => 'Anulare'; + + @override + String get androidSignInTitle => 'Autentificare necesară'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Mergi la setări'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'Ok'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Această adresă de e-mail este deja folosită'; + + @override + String emailChangedTo(String newEmail) { + return 'E-mail modificat în $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Sesiune expirată'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index 51db2ed222..52dfd4e049 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -14,40 +14,40 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Не удается подключиться к Ente, пожалуйста, повторите попытку через некоторое время. Если ошибка не устраняется, обратитесь в службу поддержки.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Похоже, что-то пошло не так. Пожалуйста, повторите попытку через некоторое время. Если ошибка повторится, обратитесь в нашу службу поддержки.'; @override - String get error => 'Error'; + String get error => 'Ошибка'; @override - String get ok => 'Ok'; + String get ok => 'Ок'; @override - String get faq => 'FAQ'; + String get faq => 'ЧаВо'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Связаться с поддержкой'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Отправить свои журналы'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Пожалуйста, отправьте журналы на \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Копировать адрес электронной почты'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Экспорт журналов'; @override - String get cancel => 'Cancel'; + String get cancel => 'Отмена'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsRu extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Сообщить об ошибке'; @override String get logsDialogBody => @@ -100,505 +88,511 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Подключено к $endpoint'; } @override - String get save => 'Save'; + String get save => 'Сохранить'; @override - String get send => 'Send'; + String get send => 'Отправить'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Вы хотите сохранить это в хранилище (папку загрузок по умолчанию) или отправить в другие приложения?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Вы хотите сохранить это в хранилище (по умолчанию папка загрузок)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Введите ваш новый адрес электронной почты'; @override - String get email => 'Email'; + String get email => 'Электронная почта'; @override - String get verify => 'Verify'; + String get verify => 'Подтвердить'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Неверный адрес электронной почты'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Пожалуйста, введите действительный адрес электронной почты.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Пожалуйста, подождите...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Подтверждение пароля'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Неправильный пароль'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Пожалуйста, попробуйте ещё раз'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Введите пароль'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Введите пароль'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активные сеансы'; @override - String get oops => 'Oops'; + String get oops => 'Ой'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Что-то пошло не так. Попробуйте еще раз'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => 'Вы выйдете из этого устройства!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Вы выйдете из списка следующих устройств:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Завершить сеанс?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Завершить'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Это устройство'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Создать аккаунт'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слабый'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Средний'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Сильный'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Удалить аккаунт'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Нам будет жаль, если вы уйдете. Вы столкнулись с какой-то проблемой?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, отправить отзыв'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Нет, удалить аккаунт'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Пожалуйста, авторизуйтесь, чтобы начать удаление аккаунта'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Подтвердить удаление аккаунта'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Эта учетная запись связана с другими приложениями Ente, если вы ими пользуетесь.\n\nЗагруженные вами данные во всех приложениях Ente будут запланированы к удалению, а ваша учетная запись будет удалена без возможности восстановления.'; @override - String get delete => 'Delete'; + String get delete => 'Удалить'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Создать новый аккаунт'; @override - String get password => 'Password'; + String get password => 'Пароль'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Подтвердить пароль'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Мощность пароля: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Как вы узнали о Ente? (необязательно)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Мы не отслеживаем установки приложений. Было бы полезно, если бы вы сказали, где нас нашли!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Я согласен с условиями предоставления услуг и политикой конфиденциальности'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Условия использования'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика конфиденциальности'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Я понимаю, что если я потеряю свой пароль, я могу потерять свои данные, так как мои данные в сквозном шифровании.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифрование'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Войти'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'С возвращением!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Нажимая на логин, я принимаю условия использования и политику конфиденциальности'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Нет подключения к Интернету'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Проверьте подключение к Интернету и повторите попытку.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Проверка не удалась, попробуйте еще раз'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Пересоздать пароль'; @override String get recreatePasswordBody => - '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).'; + 'Текущее устройство недостаточно мощно для верификации пароля, но мы можем регенерировать так, как это работает со всеми устройствами.\n\nПожалуйста, войдите, используя ваш ключ восстановления и сгенерируйте ваш пароль (вы можете использовать тот же пароль, если пожелаете).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Использовать ключ восстановления'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Забыл пароль'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Изменить адрес электронной почты'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Подтвердить адрес электронной почты'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Мы отправили письмо на $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Подтвердите адрес электронной почты, чтобы сбросить пароль.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Пожалуйста, проверьте свой почтовый ящик (и спам) для завершения верификации'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Нажмите, чтобы ввести код'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Отправить электронное письмо'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Отправить письмо еще раз'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Верификация еще не завершена'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сессия недействительна'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => 'Сессия истекла. Войдите снова.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Проверка с помощью ключа доступа'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Ожидание подтверждения...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Попробовать снова'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Проверить статус'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Войти с помощью TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Восстановить аккаунт'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Поставить пароль'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Изменить пароль'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Сбросить пароль'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Ключи шифрования'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Введите пароль, который мы можем использовать для шифрования ваших данных'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Введите новый пароль, который мы можем использовать для шифрования ваших данных'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Мы не храним этот пароль, поэтому если вы забудете его, мы не сможем расшифровать ваши данные'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Как это работает'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Генерируем ключи шифрования...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Пароль успешно изменён'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Выйти из других устройств'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Если вы думаете, что кто-то может знать ваш пароль, вы можете принудительно выйти из всех устройств.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Выйти из других устройств'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не выходить'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Генерируем ключи шифрования...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Далее'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Небезопасное устройство'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'К сожалению, мы не смогли сгенерировать безопасные ключи на этом устройстве.\n\nПожалуйста, зарегистрируйтесь с другого устройства.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Ключ восстановления скопирован в буфер обмена'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Ключ восстановления'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Если вы забыли свой пароль, то восстановить данные можно только с помощью этого ключа.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Мы не храним этот ключ, пожалуйста, сохраните этот ключ в безопасном месте.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Сделать позже'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Сохранить ключ'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Ключ восстановления сохранён в папке Загрузки!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Нет ключа восстановления?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Двухфакторная аутентификация'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Введите 6-значный код из\nвашего приложения-аутентификатора'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Потеряно устройство?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Введите ключ восстановления'; @override - String get recover => 'Recover'; + String get recover => 'Восстановить'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Выходим...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Немедленно'; @override - String get appLock => 'App lock'; + String get appLock => 'Блокировка приложения'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Автоблокировка'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Системная блокировка не найдена'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Чтобы включить блокировку устройства, пожалуйста, настройте пароль или блокировку экрана в настройках системы.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Выберите между экраном блокировки вашего устройства и пользовательским экраном блокировки с PIN-кодом или паролем.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Блокировка устройства'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pin блокировка'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Время в фоне, после которого приложение блокируется'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Скрыть содержимое'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Скрывает содержимое приложения в переключателе приложений и отключает скриншоты'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Скрывает содержимое приложения в переключателе приложений'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Слишком много неудачных попыток'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Нажмите для разблокировки'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Вы уверены, что хотите выйти?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Да, выйти'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Пожалуйста, авторизуйтесь для просмотра ваших секретов'; @override - String get next => 'Next'; + String get next => 'Далее'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Задать новый пароль'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Введите PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Установите новый PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Подтвердить'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Подтвердите пароль'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Введите PIN-код ещё раз'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Подтвердите личность'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Не распознано. Попробуйте еще раз.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Успешно'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Отменить'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Требуется аутентификация'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Требуется биометрия'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Требуются учетные данные устройства'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Требуются учетные данные устройства'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Перейдите к настройкам'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Биометрическая аутентификация не настроена на вашем устройстве. Перейдите в \"Настройки > Безопасность\", чтобы добавить биометрическую аутентификацию.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Биометрическая аутентификация отключена. Пожалуйста, заблокируйте и разблокируйте экран, чтобы включить ее.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'ОК'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => + 'Адрес электронной почты уже зарегистрирован.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => + 'Адрес электронной почты не зарегистрирован.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => + 'Этот адрес электронной почты уже используется'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Адрес электронной почты изменен на $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Аутентификация не удалась, попробуйте еще раз'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Аутентификация прошла успешно!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Сеанс истек'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Неправильный ключ восстановления'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Введен неправильный ключ восстановления'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Двухфакторная аутентификация успешно сброшена'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +605,28 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Срок действия вашего проверочного кода истек'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Неверный код'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Извините, введенный вами код неверный'; + + @override + String get developerSettings => 'Настройки разработчика'; + + @override + String get serverEndpoint => 'Конечная точка сервера'; + + @override + String get invalidEndpoint => 'Неверная конечная точка'; + + @override + String get invalidEndpointMessage => + 'Извините, введенная вами конечная точка неверна. Пожалуйста, введите корректную конечную точку и повторите попытку.'; + + @override + String get endpointUpdatedMessage => 'Конечная точка успешно обновлена'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 0ac667a6b9..6ab32314df 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -14,40 +14,40 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Nemožno sa pripojiť k Ente, skúste znova v krátkom čase. Ak chyba pretrváva, kontaktujte podporu.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Vyzerá to, že sa niečo pokazilo. Skúste znova v krátkom čase. Ak chyba pretrváva, kontaktujte náš tím podpory.'; @override - String get error => 'Error'; + String get error => 'Chyba'; @override String get ok => 'Ok'; @override - String get faq => 'FAQ'; + String get faq => 'Často kladené otázky'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Kontaktovať podporu'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Odoslať vaše logy emailom'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Prosím, pošlite logy na adresu \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Skopírovať e-mailovú adresu'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exportovať logy'; @override - String get cancel => 'Cancel'; + String get cancel => 'Zrušiť'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsSk extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Nahlásiť chybu'; @override String get logsDialogBody => @@ -100,22 +88,22 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Pripojený k endpointu $endpoint'; } @override - String get save => 'Save'; + String get save => 'Uložiť'; @override - String get send => 'Send'; + String get send => 'Odoslať'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory) alebo to odoslať do iných aplikácií?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -124,443 +112,446 @@ class StringsLocalizationsSk extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Overiť'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Neplatná emailová adresa'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Zadajte platnú e-mailovú adresu.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Prosím počkajte...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Potvrďte heslo'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nesprávne heslo'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Prosím, skúste to znova'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Zadajte heslo'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Zadajte vaše heslo'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktívne relácie'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Niečo sa pokazilo, skúste to prosím znova'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Toto vás odhlási z tohto zariadenia!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Toto vás odhlási z následujúceho zariadenia:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Ukončiť reláciu?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Ukončiť'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Toto zariadenie'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Vytvoriť účet'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Slabé'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Mierne'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silné'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Odstrániť účet'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Bude nám ľúto ak odídeš. Máš nejaký problém?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Áno, odoslať spätnú väzbu'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nie, odstrániť účet'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Je potrebné overenie pre spustenie odstránenia účtu'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potvrď odstránenie účtu'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používaš.\n\nTvoje nahrané údaje vo všetkých Ente aplikáciách budú naplánované na odstránenie a tvoj účet bude natrvalo odstránený.'; @override - String get delete => 'Delete'; + String get delete => 'Odstrániť'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Vytvoriť nový účet'; @override - String get password => 'Password'; + String get password => 'Heslo'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potvrdiť heslo'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Sila hesla: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ako ste sa dozvedeli o Ente? (voliteľné)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nesledujeme inštalácie aplikácie. Veľmi by nám pomohlo, keby ste nám povedali, ako ste sa o nás dozvedeli!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Podmienky používania'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Zásady ochrany osobných údajov'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumiem, že ak stratím alebo zabudnem heslo, môžem stratiť svoje údaje, pretože moje údaje sú šifrované end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifrovanie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Prihlásenie'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Vitajte späť!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Kliknutím na prihlásenie, súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Žiadne internetové pripojenie'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Skontrolujte svoje internetové pripojenie a skúste to znova.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Overenie zlyhalo, skúste to znova'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Resetovať heslo'; @override String get recreatePasswordBody => - '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).'; + 'Aktuálne zariadenie nie je dostatočne výkonné na overenie vášho hesla, avšak vieme ho regenerovať spôsobom, ktorý funguje vo všetkých zariadeniach.\n\nPrihláste sa pomocou kľúča na obnovenie a znovu vygenerujte svoje heslo (ak si prajete, môžete znova použiť rovnaké).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Použiť kľúč na obnovenie'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Zabudnuté heslo'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Zmeniť e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Overiť email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Odoslali sme email na adresu $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Ak chcete obnoviť svoje heslo, najskôr overte svoj email.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Skontrolujte svoju doručenú poštu (a spam) pre dokončenie overenia'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Klepnutím zadajte kód'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Odoslať email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Znovu odoslať email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Overenie stále prebieha'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Relácia vypršala'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Vaša relácia vypršala. Prosím, prihláste sa znovu.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Overenie pomocou passkey'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Čakanie na overenie...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Skúsiť znova'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Overiť stav'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Prihlásenie pomocou TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Obnoviť účet'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nastaviť heslo'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Zmeniť heslo'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Obnoviť heslo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifrovacie kľúče'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Zadajte heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Zadajte nové heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Ente neukladá tohto heslo. V prípade, že ho zabudnete, nie sme schopní rozšifrovať vaše údaje'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Ako to funguje'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generovanie šifrovacích kľúčov...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Heslo bolo úspešne zmenené'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Odhlásiť sa z iných zariadení'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ak si myslíš, že by niekto mohol poznať tvoje heslo, môžeš vynútiť odhlásenie všetkých ostatných zariadení používajúcich tvoj účet.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Odhlásiť iné zariadenie'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neodhlasovať'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Generovanie šifrovacích kľúčov...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Pokračovať'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Slabo zabezpečené zariadenie'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Ospravedlňujeme sa, v tomto zariadení sme nemohli generovať bezpečnostné kľúče.\n\nzaregistrujte sa z iného zariadenia.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Skopírovaný kód pre obnovenie do schránky'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kľúč pre obnovenie'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ak zabudnete heslo, jediným spôsobom, ako môžete obnoviť svoje údaje, je tento kľúč.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'My tento kľúč neuchovávame, uložte si tento kľúč obsahujúci 24 slov na bezpečnom mieste.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Urobiť to neskôr'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Uložiť kľúč'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kľúč na obnovenie uložený v priečinku Stiahnutých súborov!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Nemáte kľúč pre obnovenie?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvojfaktorové overovanie'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Zadajte 6-miestny kód z\nvašej overovacej aplikácie'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Stratené zariadenie?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Vložte váš kód pre obnovenie'; @override - String get recover => 'Recover'; + String get recover => 'Obnoviť'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Odhlasovanie...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Okamžite'; @override - String get appLock => 'App lock'; + String get appLock => 'Zámok aplikácie'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatické uzamknutie'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Nenájdená žiadna zámka obrazovky'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Pre povolenie zámku zariadenia, nastavte prístupový kód zariadenia alebo zámok obrazovky v nastaveniach systému.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Vyberte si medzi predvolenou zámkou obrazovky vášho zariadenia a vlastnou zámkou obrazovky s PIN kódom alebo heslom.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Zámok zariadenia'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Zámok PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Čas, po ktorom sa aplikácia uzamkne po nečinnosti'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Skryť obsah'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Skrýva obsah v prepínači aplikácii a zakazuje snímky obrazovky'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'Skrýva obsah v prepínači aplikácii'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Príliš veľa chybných pokusov'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Ťuknutím odomknete'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Naozaj sa chcete odhlásiť?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Áno, odhlásiť sa'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Pre zobrazenie vašich tajných údajov sa musíte overiť'; @override - String get next => 'Next'; + String get next => 'Ďalej'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Nastaviť nové heslo'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Zadajte PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Nastaviť nový PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Potvrdiť'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Zadajte heslo znova'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Zadajte PIN znova'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Overiť identitu'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Nerozpoznané. Skúste znova.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Overenie úspešné'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Zrušiť'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Vyžaduje sa overenie'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Vyžaduje sa biometria'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Vyžadujú sa poverenia zariadenia'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Vyžadujú sa poverenia zariadenia'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Prejsť do nastavení'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Overenie pomocou biometrie nie je na vašom zariadení nastavené. Prejdite na \'Nastavenie > Zabezpečenie\' a pridajte overenie pomocou biometrie.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Overenie pomocou biometrie je zakázané. Zamknite a odomknite svoju obrazovku, aby ste ho povolili.'; @override String get iOSOkButton => 'OK'; @@ -572,33 +563,33 @@ class StringsLocalizationsSk extends StringsLocalizations { String get emailNotRegistered => 'Email not registered.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Tento e-mail sa už používa'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Emailová adresa bola zmenená na $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Overenie zlyhalo. Skúste to znova'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Overenie sa podarilo!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Relácia vypršala'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Nesprávny kľúč na obnovenie'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Kľúč na obnovenie, ktorý ste zadali, je nesprávny'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Dvojfaktorové overovanie bolo úspešne obnovené'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,340 +602,28 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Platnosť overovacieho kódu uplynula'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Neplatný kód'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Ľutujeme, zadaný kód je nesprávny'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get developerSettings => 'Nastavenia pre vývojárov'; @override - String get email => 'Email'; + String get serverEndpoint => 'Endpoint servera'; @override - String get verify => 'Verify'; + String get invalidEndpoint => 'Neplatný endpoint'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEndpointMessage => + 'Ospravedlňujeme sa, endpoint, ktorý ste zadali, je neplatný. Zadajte platný endpoint a skúste to znova.'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; - - @override - String get pleaseWait => 'Please wait...'; - - @override - String get verifyPassword => 'Verify password'; - - @override - String get incorrectPasswordTitle => 'Incorrect password'; - - @override - String get pleaseTryAgain => 'Please try again'; - - @override - String get enterPassword => 'Enter password'; - - @override - String get enterYourPasswordHint => 'Enter your password'; - - @override - String get activeSessions => 'Active sessions'; - - @override - String get oops => 'Oops'; - - @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; - - @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; - - @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; - - @override - String get terminateSession => 'Terminate session?'; - - @override - String get terminate => 'Terminate'; - - @override - String get thisDevice => 'This device'; - - @override - String get createAccount => 'Create account'; - - @override - String get weakStrength => 'Weak'; - - @override - String get moderateStrength => 'Moderate'; - - @override - String get strongStrength => 'Strong'; - - @override - String get deleteAccount => 'Delete account'; - - @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; - - @override - String get yesSendFeedbackAction => 'Yes, send feedback'; - - @override - String get noDeleteAccountAction => 'No, delete account'; - - @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; - - @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; - - @override - String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; - - @override - String get delete => 'Delete'; - - @override - String get createNewAccount => 'Create new account'; - - @override - String get password => 'Password'; - - @override - String get confirmPassword => 'Confirm password'; - - @override - String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; - } - - @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; - - @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; - - @override - String get signUpTerms => - 'I agree to the terms of service and privacy policy'; - - @override - String get termsOfServicesTitle => 'Terms'; - - @override - String get privacyPolicyTitle => 'Privacy Policy'; - - @override - String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; - - @override - String get encryption => 'Encryption'; - - @override - String get logInLabel => 'Log in'; - - @override - String get welcomeBack => 'Welcome back!'; - - @override - String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; - - @override - String get noInternetConnection => 'No internet connection'; - - @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; - - @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; - - @override - String get recreatePasswordTitle => 'Recreate password'; - - @override - String get recreatePasswordBody => - '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).'; - - @override - String get useRecoveryKey => 'Use recovery key'; - - @override - String get forgotPassword => 'Forgot password'; - - @override - String get changeEmail => 'Change email'; - - @override - String get verifyEmail => 'Verify email'; - - @override - String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; - } - - @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; - - @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; - - @override - String get tapToEnterCode => 'Tap to enter code'; - - @override - String get sendEmail => 'Send email'; - - @override - String get resendEmail => 'Resend email'; - - @override - String get passKeyPendingVerification => 'Verification is still pending'; - - @override - String get loginSessionExpired => 'Session expired'; - - @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; - - @override - String get passkeyAuthTitle => 'Passkey verification'; - - @override - String get waitingForVerification => 'Waiting for verification...'; - - @override - String get tryAgain => 'Try again'; - - @override - String get checkStatus => 'Check status'; - - @override - String get loginWithTOTP => 'Login with TOTP'; - - @override - String get recoverAccount => 'Recover account'; - - @override - String get setPasswordTitle => 'Set password'; - - @override - String get changePasswordTitle => 'Change password'; - - @override - String get resetPasswordTitle => 'Reset password'; - - @override - String get encryptionKeys => 'Encryption keys'; - - @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; - - @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; - - @override - String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; - - @override - String get howItWorks => 'How it works'; - - @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; - - @override - String get passwordChangedSuccessfully => 'Password changed successfully'; - - @override - String get signOutFromOtherDevices => 'Sign out from other devices'; - - @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; - - @override - String get signOutOtherDevices => 'Sign out other devices'; - - @override - String get doNotSignOut => 'Do not sign out'; - - @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; - - @override - String get continueLabel => 'Continue'; - - @override - String get insecureDevice => 'Insecure device'; - - @override - String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; - - @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; - - @override - String get recoveryKey => 'Recovery key'; - - @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; - - @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; - - @override - String get doThisLater => 'Do this later'; - - @override - String get saveKey => 'Save key'; - - @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; - - @override - String get noRecoveryKeyTitle => 'No recovery key?'; - - @override - String get twoFactorAuthTitle => 'Two-factor authentication'; - - @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; - - @override - String get lostDeviceTitle => 'Lost device?'; - - @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; - - @override - String get recover => 'Recover'; + String get endpointUpdatedMessage => 'Endpoint úspešne aktualizovaný'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sl.dart new file mode 100644 index 0000000000..8571b1983f --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sl.dart @@ -0,0 +1,629 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Slovenian (`sl`). +class StringsLocalizationsSl extends StringsLocalizations { + StringsLocalizationsSl([String locale = 'sl']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Ne morete se povezati z Ente, preverite omrežne nastavitve in se obrnite na podporo, če se napaka nadaljuje.'; + + @override + String get networkConnectionRefusedErr => + 'Ne morete se povezati z Ente, poskusite znova čez nekaj časa. Če se napaka nadaljuje, se obrnite na podporo.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Zdi se, da je šlo nekaj narobe. Po določenem času poskusite znova. Če se napaka nadaljuje, se obrnite na našo ekipo za podporo.'; + + @override + String get error => 'Napaka'; + + @override + String get ok => 'V redu'; + + @override + String get faq => 'Pogosta vprašanja'; + + @override + String get contactSupport => 'Stik s podporo'; + + @override + String get emailYourLogs => 'Pošlji loge po e-pošti'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Loge pošljite na naslov \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Kopiraj e-poštni naslov'; + + @override + String get exportLogs => 'Izvozi loge'; + + @override + String get cancel => 'Prekliči'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Prijavite napako'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Povezano na $endpoint'; + } + + @override + String get save => 'Shrani'; + + @override + String get send => 'Pošlji'; + + @override + String get saveOrSendDescription => + 'Želite to shraniti v shrambo (privzeto: mapa Prenosi) ali poslati drugim aplikacijam?'; + + @override + String get saveOnlyDescription => + 'Želite to shraniti v shrambo (privzeto: mapa Prenosi)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'E-pošta'; + + @override + String get verify => 'Preveri'; + + @override + String get invalidEmailTitle => 'Neveljaven e-poštni naslov'; + + @override + String get invalidEmailMessage => 'Prosimo vnesite veljaven e-poštni naslov.'; + + @override + String get pleaseWait => 'Prosim počakajte...'; + + @override + String get verifyPassword => 'Potrdite geslo'; + + @override + String get incorrectPasswordTitle => 'Nepravilno geslo'; + + @override + String get pleaseTryAgain => 'Prosimo, poskusite ponovno'; + + @override + String get enterPassword => 'Vnesite geslo'; + + @override + String get enterYourPasswordHint => 'Vnesite svoje geslo'; + + @override + String get activeSessions => 'Aktivne seje'; + + @override + String get oops => 'Ups'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Nekaj je šlo narobe, prosimo poizkusite znova.'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'To vas bo odjavilo iz te naprave!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'To vas bo odjavilo iz naslednje naprave:'; + + @override + String get terminateSession => 'Končaj sejo'; + + @override + String get terminate => 'Končaj'; + + @override + String get thisDevice => 'Ta naprava'; + + @override + String get createAccount => 'Ustvari račun'; + + @override + String get weakStrength => 'Šibko'; + + @override + String get moderateStrength => 'Zmerno'; + + @override + String get strongStrength => 'Močno'; + + @override + String get deleteAccount => 'Izbriši račun'; + + @override + String get deleteAccountQuery => + 'Žal nam je, da odhajate. Imate kakšne težave?'; + + @override + String get yesSendFeedbackAction => 'Ja, pošlji povratne informacije'; + + @override + String get noDeleteAccountAction => 'Ne, izbriši račun'; + + @override + String get initiateAccountDeleteTitle => 'Za izbris računa, se overite'; + + @override + String get confirmAccountDeleteTitle => 'Potrdi brisanje računa'; + + @override + String get confirmAccountDeleteMessage => + 'Ta račun je povezan z drugimi aplikacijami Ente, če jih uporabljate.\n\nVaši naloženi podatki v vseh aplikacijah Ente bodo načrtovane za izbris, vaš račun pa bo trajno izbrisan.'; + + @override + String get delete => ''; + + @override + String get createNewAccount => 'Ustvari nov račun'; + + @override + String get password => 'Geslo'; + + @override + String get confirmPassword => 'Potrdi geslo'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Moč gesla: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'Kako ste slišali o Ente? (izbirno)'; + + @override + String get hearUsExplanation => + 'Namestitvam aplikacij ne sledimo. Pomagalo bi, če bi nam povedali, kje ste nas našli!'; + + @override + String get signUpTerms => + 'Strinjam se s pogoji uporabe in politiko zasebnosti'; + + @override + String get termsOfServicesTitle => 'Pogoji uporabe'; + + @override + String get privacyPolicyTitle => 'Politika zasebnosti'; + + @override + String get ackPasswordLostWarning => + 'Razumem, da lahko z izgubo gesla, izgubim svoje podatke, saj so end-to-end šifrirani'; + + @override + String get encryption => 'Šifriranje'; + + @override + String get logInLabel => 'Prijava'; + + @override + String get welcomeBack => 'Dobrodošli nazaj!'; + + @override + String get loginTerms => + 'S klikom na prijava, se strinjam s pogoji uporabe in politiko zasebnosti'; + + @override + String get noInternetConnection => 'Ni internetne povezave'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Preverite internetno povezavo in poskusite znova.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Potrjevanje ni bilo uspešno, prosimo poskusite znova.'; + + @override + String get recreatePasswordTitle => 'Ponovno ustvarite geslo'; + + @override + String get recreatePasswordBody => + 'Trenutna naprava, ni dovolj zmogljiva za preverjanje vašega gesla, a ga lahko generiramo na način, ki deluje z vsemi napravami.\n\nProsimo, prijavite se z vašim ključem za obnovo in ponovno ustvarite geslo (če želite lahko uporabite enako kot prej).'; + + @override + String get useRecoveryKey => 'Uporabi ključ za obnovo'; + + @override + String get forgotPassword => 'Pozabljeno geslo'; + + @override + String get changeEmail => 'Sprememba e-poštnega naslova'; + + @override + String get verifyEmail => 'Potrdite e-pošto'; + + @override + String weHaveSendEmailTo(String email) { + return 'Poslali smo e-pošto na $email'; + } + + @override + String get toResetVerifyEmail => + 'Če želite ponastaviti geslo, najprej potrdite svoj e-poštni naslov.'; + + @override + String get checkInboxAndSpamFolder => + 'Prosimo, preverite svoj e-poštni predal (in nezaželeno pošto), da končate verifikacijo'; + + @override + String get tapToEnterCode => 'Pritisni za vnos kode'; + + @override + String get sendEmail => 'Pošlji e-pošto'; + + @override + String get resendEmail => 'Ponovno pošlji e-pošto'; + + @override + String get passKeyPendingVerification => 'Preverjanje še ni zaključeno'; + + @override + String get loginSessionExpired => 'Seja je potekla'; + + @override + String get loginSessionExpiredDetails => + 'Vaša seja je potekla. Prosimo ponovno se prijavite.'; + + @override + String get passkeyAuthTitle => 'Potrditev ključa za dostop (passkey)'; + + @override + String get waitingForVerification => 'Čakanje na potrditev...'; + + @override + String get tryAgain => 'Poskusite ponovno'; + + @override + String get checkStatus => 'Preveri status'; + + @override + String get loginWithTOTP => 'Prijava z TOTP'; + + @override + String get recoverAccount => 'Obnovi račun'; + + @override + String get setPasswordTitle => 'Nastavite geslo'; + + @override + String get changePasswordTitle => 'Sprememba gesla'; + + @override + String get resetPasswordTitle => 'Ponastavitev gesla'; + + @override + String get encryptionKeys => 'Šifrirni ključi'; + + @override + String get enterPasswordToEncrypt => + 'Vnesite geslo, ki ga lahko uporabimo za šifriranje vaših podatkov'; + + @override + String get enterNewPasswordToEncrypt => + 'Vnesite novo geslo, ki ga lahko uporabimo za šifriranje vaših podatkov'; + + @override + String get passwordWarning => + 'Tega gesla ne shranjujemo, zato v primeru, da ga pozabite, ne moremo dešifrirati vaših podatkov.'; + + @override + String get howItWorks => 'Kako deluje? '; + + @override + String get generatingEncryptionKeys => 'Ustvarjanje ključe za šifriranje'; + + @override + String get passwordChangedSuccessfully => 'Geslo je bilo uspešno spremenjeno'; + + @override + String get signOutFromOtherDevices => 'Odjavi se iz ostalih naprav'; + + @override + String get signOutOtherBody => + 'Če menite, da bi lahko kdo poznal vaše geslo, lahko vse druge naprave, ki uporabljajo vaš račun, prisilite, da se odjavijo.'; + + @override + String get signOutOtherDevices => 'Odjavi ostale naprave'; + + @override + String get doNotSignOut => 'Ne odjavi se'; + + @override + String get generatingEncryptionKeysTitle => 'Generiramo ključe za šifriranje'; + + @override + String get continueLabel => 'Nadaljuj'; + + @override + String get insecureDevice => 'Nezanesljiva naprava'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Žal v tej napravi nismo mogli ustvariti varnih ključev.\n\nProsimo, prijavite se iz druge naprave.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Ključ za obnovo kopiran v odložišče'; + + @override + String get recoveryKey => 'Ključ za obnovitev'; + + @override + String get recoveryKeyOnForgotPassword => + 'Če pozabite svoje geslo, je edini način da obnovite svoje podatke s tem ključem'; + + @override + String get recoveryKeySaveDescription => + 'Tega ključa ne hranimo, prosimo shranite teh 24 besed na varnem'; + + @override + String get doThisLater => 'Stori to kasneje'; + + @override + String get saveKey => 'Shrani ključ'; + + @override + String get recoveryKeySaved => + 'Ključ za obnovitev je shranjen v mapi Prenosi!'; + + @override + String get noRecoveryKeyTitle => 'Nimate ključa za obnovo?'; + + @override + String get twoFactorAuthTitle => 'Dvojno preverjanja pristnosti'; + + @override + String get enterCodeHint => + 'Vnesite 6 mestno kodo iz vaše aplikacije za preverjanje pristnosti'; + + @override + String get lostDeviceTitle => 'Izgubljena naprava?'; + + @override + String get enterRecoveryKeyHint => 'Vnesite vaš ključ za obnovitev'; + + @override + String get recover => 'Obnovi'; + + @override + String get loggingOut => 'Odjavljanje...'; + + @override + String get immediately => 'Takoj'; + + @override + String get appLock => 'Zaklep aplikacije'; + + @override + String get autoLock => 'Samodejno zaklepanje'; + + @override + String get noSystemLockFound => 'Nobeno zaklepanje sistema ni bilo najdeno'; + + @override + String get deviceLockEnablePreSteps => + 'Da omogočite zaklepanje naprave, prosimo nastavite kodo ali zaklepanje zaslona v sistemskih nastavitvah.'; + + @override + String get appLockDescription => + 'Izbirate lahko med privzetim zaklenjenim zaslonom naprave in zaklenjenim zaslonom po meri s kodo PIN ali geslom.'; + + @override + String get deviceLock => 'Zaklepanje naprave'; + + @override + String get pinLock => 'Zaklepanje s PIN'; + + @override + String get autoLockFeatureDescription => + 'Čas po katerem se aplikacije zaklene, ko jo enkrat zapustite.'; + + @override + String get hideContent => 'Skrij vsebino'; + + @override + String get hideContentDescriptionAndroid => + 'Skrije vsebino aplikacije v menjalniku opravil in onemogoči posnetke zaslona'; + + @override + String get hideContentDescriptioniOS => + 'Skrije vsebino aplikacije v menjalniku opravil'; + + @override + String get tooManyIncorrectAttempts => 'Preveč nepravilnih poskusov'; + + @override + String get tapToUnlock => 'Kliknite za odklepanje'; + + @override + String get areYouSureYouWantToLogout => + 'Ali ste prepričani, da se želite odjaviti?'; + + @override + String get yesLogout => 'Ja, odjavi se'; + + @override + String get authToViewSecrets => + 'Če si želite ogledati svoje skrivne ključe, se overite'; + + @override + String get next => 'Naprej'; + + @override + String get setNewPassword => 'Nastavi novo geslo'; + + @override + String get enterPin => 'Vnesi PIN'; + + @override + String get setNewPin => 'Nastavi nov PIN'; + + @override + String get confirm => 'Potrdi'; + + @override + String get reEnterPassword => 'Ponovno vnesite geslo'; + + @override + String get reEnterPin => 'Ponovno vnesite PIN'; + + @override + String get androidBiometricHint => 'Potrdite identiteto'; + + @override + String get androidBiometricNotRecognized => 'Ni prepoznano. Poskusite znova.'; + + @override + String get androidBiometricSuccess => 'Uspešno'; + + @override + String get androidCancelButton => 'Prekliči'; + + @override + String get androidSignInTitle => 'Potrebna je overitev'; + + @override + String get androidBiometricRequiredTitle => 'Zahtevani biometrični podatki'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Zahtevani podatki za vpis v napravo'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Zahtevani podatki za vpis v napravo'; + + @override + String get goToSettings => 'Pojdi v nastavitve'; + + @override + String get androidGoToSettingsDescription => + 'Biometrično overjanje v vaši napravi ni nastavljeno. Pojdite v \"Nastavitve > Varnost\" in dodajte biometrično overjanje.'; + + @override + String get iOSLockOut => + 'Biometrično overjanje je onemogočeno. Če ga želite omogočiti, zaklenite in odklenite zaslon.'; + + @override + String get iOSOkButton => 'V redu'; + + @override + String get emailAlreadyRegistered => 'E-poštni naslov je že registriran.'; + + @override + String get emailNotRegistered => 'E-poštni naslov ni registriran.'; + + @override + String get thisEmailIsAlreadyInUse => 'Ta e-poštni naslove je že v uporabi.'; + + @override + String emailChangedTo(String newEmail) { + return 'E-poštni naslove je bil spremenjen na $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Overitev ni uspela, prosimo poskusite znova'; + + @override + String get authenticationSuccessful => 'Overitev uspešna!'; + + @override + String get sessionExpired => 'Seja je potekla'; + + @override + String get incorrectRecoveryKey => 'Nepravilen ključ za obnovitev'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Ključ za obnovitev, ki ste ga vnesli ni pravilen'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Uspešna ponastavitev dvostopenjske avtentikacije'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Vaša koda za potrditev je potekla.'; + + @override + String get incorrectCode => 'Nepravilna koda'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Oprostite, koda ki ste jo vnesli ni pravilna'; + + @override + String get developerSettings => 'Nastavitve za razvijalce'; + + @override + String get serverEndpoint => 'Endpoint strežnika'; + + @override + String get invalidEndpoint => 'Nepravilen endpoint'; + + @override + String get invalidEndpointMessage => + 'Oprostite endpoint, ki ste ga vnesli ni bil pravilen. Prosimo, vnesite pravilen endpoint in poskusite znova.'; + + @override + String get endpointUpdatedMessage => 'Endpoint posodobljen uspešno'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 04a4352ad8..3a2c2fc4f7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -14,40 +14,40 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Није могуће повезивање са Ente-ом, покушајте поново мало касније. Ако грешка настави, обратите се подршци.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Изгледа да је нешто погрешно. Покушајте поново након неког времена. Ако грешка настави, обратите се нашем тиму за подршку.'; @override - String get error => 'Error'; + String get error => 'Грешка'; @override - String get ok => 'Ok'; + String get ok => 'У реду'; @override - String get faq => 'FAQ'; + String get faq => 'Питања'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Контактирати подршку'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Имејлирајте извештаје'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Пошаљите извештаје на \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Копирати имејл адресу'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Извези изештаје'; @override - String get cancel => 'Cancel'; + String get cancel => 'Откажи'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsSr extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Пријави грешку'; @override String get logsDialogBody => @@ -100,505 +88,510 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Везано за $endpoint'; } @override - String get save => 'Save'; + String get save => 'Сачувај'; @override - String get send => 'Send'; + String get send => 'Пошаљи'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано) или да га пошаљете другим апликацијама?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Унесите Ваш нови имејл'; @override - String get email => 'Email'; + String get email => 'Имејл'; @override - String get verify => 'Verify'; + String get verify => 'Верификуј'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Погрешна имејл адреса'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Унесите важећи имејл.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Молимо сачекајте...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Верификујте лозинку'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Неисправна лозинка'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Пробајте поново'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Унеси лозинку'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Унесите лозинку'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активне сесије'; @override - String get oops => 'Oops'; + String get oops => 'Упс'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Нешто је пошло наопако. Покушајте поново'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Ово ће вас одјавити из овог уређаја!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Ово ће вас одјавити из овог уређаја:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Прекинути сесију?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Прекини'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Овај уређај'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Направи налог'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слабо'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Умерено'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Јако'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Избриши налог'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Жао нам је што одлазите. Да ли се суочавате са неком грешком?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, послати повратне информације'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Не, избрисати налог'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Молимо вас да се аутентификујете за брисање рачуна'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Потврда брисања рачуна'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Овај налог је повезан са другим Ente апликацијама, ако користите било коју.\n\nВаши преношени подаци, на свим Ente апликацијама биће заказани за брисање, и ваш рачун ће се трајно избрисати.'; @override - String get delete => 'Delete'; + String get delete => 'Обриши'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Креирај нови налог'; @override - String get password => 'Password'; + String get password => 'Лозинка'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Потврдите лозинку'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Снага лозинке: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Како сте чули о Ente? (опционо)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Не пратимо инсталацију апликације. Помогло би да нам кажеш како си нас нашао!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Прихватам услове сервиса и политику приватности'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Услови'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика приватности'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Разумем да ако изгубим лозинку, могу изгубити своје податке пошто су шифрирани од краја до краја.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифровање'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Пријави се'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Добродошли назад!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Кликом на пријаву, прихватам услове сервиса и политику приватности'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Нема интернет везе'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Провери своју везу са интернетом и покушај поново.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Неуспешна верификација, покушајте поново'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Поново креирати лозинку'; @override String get recreatePasswordBody => - '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).'; + 'Тренутни уређај није довољно моћан да потврди вашу лозинку, али можемо регенерирати на начин који ради са свим уређајима.\n\nПријавите се помоћу кључа за опоравак и обновите своју лозинку (можете поново користити исту ако желите).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Користите кључ за опоравак'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Заборавио сам лозинку'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Промени имејл'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Потврди имејл'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Послали смо имејл на $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Да бисте ресетовали лозинку, прво потврдите свој имејл.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Молимо вас да проверите примљену пошту (и нежељену пошту) да бисте довршили верификацију'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Пипните да бисте унели кôд'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Шаљи имејл'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Поново послати имејл'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Верификација је још у току'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сесија је истекла'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Ваша сесија је истекла. Молимо пријавите се поново.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Верификација сигурносном кључем'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Чека се верификација...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Покушај поново'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Провери статус'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Пријава са TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Опоравак налога'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Постави лозинку'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Промени лозинку'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Ресетуј лозинку'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Кључеве шифровања'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Унесите лозинку за употребу за шифровање ваших података'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Унесите нову лозинку за употребу за шифровање ваших података'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Не чувамо ову лозинку, па ако је заборавите, не можемо дешифрирати ваше податке'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Како то функционише'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Генерисање кључева за шифровање...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Лозинка је успешно промењена'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Одјави се из других уређаја'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ако мислиш да неко може знати твоју лозинку, можеш приморати одјављивање све остале уређаје које користе твој налог.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Одјави друге уређаје'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не одјави'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Генерисање кључева за шифровање...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Настави'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Уређај није сигуран'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Извините, не можемо да генеришемо сигурне кључеве на овом уређају.\n\nМолимо пријавите се са другог уређаја.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Кључ за опоравак копирано у остави'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Резервни Кључ'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ако заборавите лозинку, једини начин на који можете повратити податке је са овим кључем.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Не чувамо овај кључ, молимо да сачувате кључ од 24 речи на сигурном месту.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Уради то касније'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Сачувај кључ'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Кључ за опоравак сачуван у фасцикли за преузимање!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Немате кључ за опоравак?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Дво-факторска аутентификација'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Унесите 6-цифрени кôд из\nапликације за аутентификацију'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Узгубили сте уређај?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Унети кључ за опоравак'; @override - String get recover => 'Recover'; + String get recover => 'Опорави'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Одјављивање...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Одмах'; @override - String get appLock => 'App lock'; + String get appLock => 'Закључавање апликације'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Ауто-закључавање'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Није пронађено ниједно закључавање система'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Да бисте омогућили закључавање уређаја, молимо вас да подесите шифру уређаја или закључавање екрана у системским подешавањима.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Изаберите између заданог закључавање екрана вашег уређаја и прилагођени екран за закључавање са ПИН-ом или лозинком.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Закључавање уређаја'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'ПИН клокирање'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Време након којег се апликација блокира након што је постављенеа у позадину'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Сакриј садржај'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Сакрива садржај апликације у пребацивање апликација и онемогућује снимке екрана'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Сакрива садржај апликације у пребацивање апликација'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Превише погрешних покушаја'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Додирните да бисте откључали'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Да ли сте сигурни да се одјавите?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Да, одјави ме'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Аутентификујте се да бисте прегледали Ваше тајне'; @override - String get next => 'Next'; + String get next => 'Следеће'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Постави нову лозинку'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Унеси ПИН'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Постави нови ПИН'; @override - String get confirm => 'Confirm'; + String get confirm => 'Потврди'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Поново унеси лозинку'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Поново унеси ПИН'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Потврдите идентитет'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Нисмо препознали. Покушати поново.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Успех'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Откажи'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Потребна аутентификација'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Потребна је биометрија'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Потребни су акредитиви уређаја'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Потребни су акредитиви уређаја'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Иди на поставке'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Биометријска аутентификација није постављена на вашем уређају. Идите на \"Подешавања> Сигурност\" да бисте додали биометријску аутентификацију.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Биометријска аутентификација је онемогућена. Закључајте и откључите екран да бисте је омогућили.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'У реду'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Имејл је већ регистрован.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Имејл није регистрован.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Овај имејл је већ у употреби'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Имејл промењен на $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Аутентификација није успела, покушајте поново'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Успешна аутентификација!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Сесија је истекла'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Нетачан кључ за опоравак'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Унети кључ за опоравак је натачан'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Двофакторска аутентификација успешно рисетирана'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +604,27 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Ваш верификациони кôд је истекао'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Погрешан кôд'; @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + String get sorryTheCodeYouveEnteredIsIncorrect => 'Унет кôд није добар'; + + @override + String get developerSettings => 'Подешавања за програмере'; + + @override + String get serverEndpoint => 'Крајња тачка сервера'; + + @override + String get invalidEndpoint => 'Погрешна крајња тачка'; + + @override + String get invalidEndpointMessage => + 'Извини, крајња тачка коју си унео је неважећа. Унеси важећу крајњу тачку и покушај поново.'; + + @override + String get endpointUpdatedMessage => 'Крајна тачка успешно ажурирана'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 0f55425c09..96d85243d0 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -14,40 +14,40 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Det gick inte att ansluta till Ente, försök igen om en stund. Om felet kvarstår, vänligen kontakta support.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Det ser ut som om något gick fel. Försök igen efter en stund. Om felet kvarstår, vänligen kontakta vår support.'; @override - String get error => 'Error'; + String get error => 'Fel'; @override - String get ok => 'Ok'; + String get ok => 'OK'; @override String get faq => 'FAQ'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Kontakta support'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Maila dina loggar'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Vänligen skicka loggarna till \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Kopiera e-postadress'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Exportera loggar'; @override - String get cancel => 'Cancel'; + String get cancel => 'Avbryt'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsSv extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Rapportera en bugg'; @override String get logsDialogBody => @@ -100,505 +88,507 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Ansluten till $endpoint'; } @override - String get save => 'Save'; + String get save => 'Spara'; @override - String get send => 'Send'; + String get send => 'Skicka'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard) eller skicka den till andra appar?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Ange din nya e-postadress'; @override - String get email => 'Email'; + String get email => 'E-post'; @override - String get verify => 'Verify'; + String get verify => 'Verifiera'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ogiltig e-postadress'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Ange en giltig e-postadress.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vänligen vänta...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bekräfta lösenord'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Felaktigt lösenord'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Försök igen'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Ange lösenord'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Ange ditt lösenord'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktiva sessioner'; @override - String get oops => 'Oops'; + String get oops => 'Hoppsan'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Något gick fel, vänligen försök igen'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Detta kommer att logga ut dig från den här enheten!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Detta kommer att logga ut dig från följande enhet:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Avsluta session?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Avsluta'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Den här enheten'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Skapa konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Svag'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Måttligt'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stark'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Radera konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Vi kommer att vara ledsna över att se dig gå. Har du något problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, skicka feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nej, radera konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Vänligen autentisera för att initiera borttagning av konto'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Bekräfta radering av kontot'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.'; @override - String get delete => 'Delete'; + String get delete => 'Radera'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Skapa nytt konto'; @override - String get password => 'Password'; + String get password => 'Lösenord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Bekräfta lösenord'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Lösenordsstyrka: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hur hörde du talas om Ente? (valfritt)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Jag samtycker till användarvillkoren och integritetspolicyn'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Villkor'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Integritetspolicy'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är end-to-end-krypterad.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kryptering'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Logga in'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Välkommen tillbaka!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Jag samtycker till användarvillkoren och integritetspolicyn'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Ingen internetanslutning'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Kontrollera din internetanslutning och försök igen.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verifiering misslyckades, vänligen försök igen'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Återskapa lösenord'; @override String get recreatePasswordBody => - '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).'; + 'Denna enhet är inte tillräckligt kraftfull för att verifiera ditt lösenord, men vi kan återskapa det på ett sätt som fungerar med alla enheter.\n\nLogga in med din återställningsnyckel och återskapa ditt lösenord (du kan använda samma igen om du vill).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Använd återställningsnyckel'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Glömt lösenord'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Ändra e-postadress'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verifiera e-postadress'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Vi har skickat ett mail till $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'För att återställa ditt lösenord måste du först bekräfta din e-postadress.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tryck för att ange kod'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Skicka e-post'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Skicka e-post igen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verifiering pågår fortfarande'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessionen har gått ut'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Din session har upphört. Logga in igen.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verifiering med inloggningsnyckel'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Väntar på verifiering...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Försök igen'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Kontrollera status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Logga in med TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Återställ konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ställ in lösenord'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Ändra lösenord'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Återställ lösenord'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Krypteringsnycklar'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Ange ett lösenord som vi kan använda för att kryptera din data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Ange ett nytt lösenord som vi kan använda för att kryptera din data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vi lagrar inte detta lösenord, så om du glömmer bort det, kan vi inte dekryptera dina data'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Så här fungerar det'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Skapar krypteringsnycklar...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Lösenordet har ändrats'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Logga ut från andra enheter'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Om du tror att någon kanske känner till ditt lösenord kan du tvinga alla andra enheter med ditt konto att logga ut.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Logga ut andra enheter'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Logga inte ut'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Skapar krypteringsnycklar...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Fortsätt'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Osäker enhet'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Återställningsnyckel kopierad till urklipp'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Återställningsnyckel'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Gör detta senare'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Spara nyckel'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Återställningsnyckel sparad i nedladdningsmappen!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Ingen återställningsnyckel?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tvåfaktorsautentisering'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Ange den 6-siffriga koden från din autentiseringsapp'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Förlorad enhet?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Ange din återställningsnyckel'; @override - String get recover => 'Recover'; + String get recover => 'Återställ'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Loggar ut...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Omedelbart'; @override - String get appLock => 'App lock'; + String get appLock => 'Applås'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Automatisk låsning'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Inget systemlås hittades'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'För att aktivera enhetslås, vänligen ställ in enhetens lösenord eller skärmlås i dina systeminställningar.'; @override String get appLockDescription => 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Enhetslås'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pinkodslås'; @override String get autoLockFeatureDescription => 'Time after which the app locks after being put in the background'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Dölj innehåll'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'Döljer appinnehåll i app-växlaren'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'För många felaktiga försök'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Tryck för att låsa upp'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Är du säker på att du vill logga ut?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Ja, logga ut'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Autentisera för att visa din återställningsnyckel'; @override - String get next => 'Next'; + String get next => 'Nästa'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Ställ in nytt lösenord'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Ange PIN-kod'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Ställ in ny PIN-kod'; @override - String get confirm => 'Confirm'; + String get confirm => 'Bekräfta'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Ange lösenord igen'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Ange PIN-kod igen'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Verifiera identitet'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Ej godkänd. Försök igen.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Slutförd'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Avbryt'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Obligatorisk autentisering'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Biometriska uppgifter krävs'; @override - String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + String get androidDeviceCredentialsRequiredTitle => 'Enhetsuppgifter krävs'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Enhetsuppgifter krävs'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Gå till inställningar'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Biometrisk autentisering är inte konfigurerad på din enhet. Gå till \"Inställningar > Säkerhet\" för att lägga till biometrisk autentisering.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biometrisk autentisering är inaktiverat. Lås och lås upp din skärm för att aktivera den.'; @override String get iOSOkButton => 'OK'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-postadress redan registrerad.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-postadress ej registrerad.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Denna e-postadress används redan'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'E-post ändrad till $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Autentisering misslyckades, vänligen försök igen'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Autentisering lyckades!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Sessionen har gått ut'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Felaktig återställningsnyckel'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Återställningsnyckeln du angav är felaktig'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Tvåfaktorsautentisering återställd'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +601,28 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Din verifieringskod har upphört att gälla'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Felaktig kod'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Tyvärr, den kod som du har angett är felaktig'; + + @override + String get developerSettings => 'Utvecklarinställningar'; + + @override + String get serverEndpoint => 'Serverns slutpunkt'; + + @override + String get invalidEndpoint => 'Ogiltig slutpunkt'; + + @override + String get invalidEndpointMessage => + 'Tyvärr, slutpunkten du angav är ogiltig. Ange en giltig slutpunkt och försök igen.'; + + @override + String get endpointUpdatedMessage => 'Slutpunkten har uppdaterats'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ti.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ti.dart new file mode 100644 index 0000000000..5f29ac5338 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ti.dart @@ -0,0 +1,626 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Tigrinya (`ti`). +class StringsLocalizationsTi extends StringsLocalizations { + StringsLocalizationsTi([String locale = 'ti']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Unable to connect to Ente, please check your network settings and contact support if the error persists.'; + + @override + String get networkConnectionRefusedErr => + 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + + @override + String get error => 'Error'; + + @override + String get ok => 'Ok'; + + @override + String get faq => 'FAQ'; + + @override + String get contactSupport => 'ደገፍ ኣድራሻ'; + + @override + String get emailYourLogs => 'Email your logs'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Please send the logs to \n$toEmail'; + } + + @override + String get copyEmailAddress => 'Copy email address'; + + @override + String get exportLogs => 'Export logs'; + + @override + String get cancel => 'Cancel'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'ጌጋ ጸብጻብ ልኣኸ'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Connected to $endpoint'; + } + + @override + String get save => 'Save'; + + @override + String get send => 'Send'; + + @override + String get saveOrSendDescription => + 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + + @override + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'በጃኻ ተጸበ...'; + + @override + String get verifyPassword => 'ቃለ-ምስጢር ኣረጋግጽ'; + + @override + String get incorrectPasswordTitle => 'ግጉይ ቃለ-ምስጢር'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'ዉዉኡ'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'ተጠቀምትና ኣይንከታተልን ኢና። ኣበይ ከም ዝረኸብካና እንተ ትነግረና ሓጋዚ እዩ፦'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'እንኳዕ ብደሓን ተመለስካ!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'ምሕዋይ መፍትሕ ተጠቐም'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'ካብ ካልኦት መሳርሒታት ኣውጽኡኒ።'; + + @override + String get doNotSignOut => 'ኣውጽኡኒ።'; + + @override + String get generatingEncryptionKeysTitle => 'ናይ ምስጢራዊ ቁልፊ ዪፍጠር...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'ምሕዋይ መፍትሕ'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; + + @override + String get loggingOut => 'ወጸ...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'ክፍለ ግዜኡ ኣኺሉ።'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index e4b060a3f1..c5be60b8db 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -14,40 +14,40 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Ente\'ye bağlanılamıyor, lütfen daha sonra tekrar deneyin. Hata devam ederse, lütfen desteğe başvurun.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Bir şeyler ters gitmiş gibi görünüyor. Lütfen bir süre sonra tekrar deneyin. Hata devam ederse, lütfen destek ekibimizle iletişime geçin.'; @override - String get error => 'Error'; + String get error => 'Hata'; @override - String get ok => 'Ok'; + String get ok => 'Tamam'; @override - String get faq => 'FAQ'; + String get faq => 'SSS'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Destek ekibiyle iletişime geçin'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Kayıtlarınızı e-postayla gönderin'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Lütfen kayıtları şu adrese gönderin\n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'E-posta adresini kopyala'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Kayıtları dışa aktar'; @override - String get cancel => 'Cancel'; + String get cancel => 'İptal Et'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsTr extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Hata bildirin'; @override String get logsDialogBody => @@ -100,505 +88,511 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Bağlandı: $endpoint'; } @override - String get save => 'Save'; + String get save => 'Kaydet'; @override - String get send => 'Send'; + String get send => 'Gönder'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Bunu belleğinize mi kaydedeceksiniz (İndirilenler klasörü varsayılandır) yoksa diğer uygulamalara mı göndereceksiniz?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Bunu belleğinize kaydetmek ister misiniz? (İndirilenler klasörü varsayılandır)'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Yeni e-posta adresinizi girin'; @override - String get email => 'Email'; + String get email => 'E-Posta'; @override - String get verify => 'Verify'; + String get verify => 'Doğrula'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Geçersiz e-posta adresi'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Lütfen geçerli bir e-posta adresi girin.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Lütfen bekleyin...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Şifreyi doğrulayın'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Yanlış şifre'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Lütfen tekrar deneyin'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Şifreyi girin'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Parolanızı girin'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktif oturumlar'; @override - String get oops => 'Oops'; + String get oops => 'Hay aksi'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Bir şeyler ters gitti, lütfen tekrar deneyin'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Bu sizin bu cihazdaki oturumunuzu kapatacaktır!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Bu, aşağıdaki cihazdan çıkış yapmanızı sağlayacaktır:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Oturumu sonlandır?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Sonlandır'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Bu cihaz'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Hesap oluştur'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Zayıf'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Orta'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Güçlü'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Hesabı sil'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Sizin gittiğinizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Evet, geri bildirimi gönder'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Hayır, hesabı sil'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Hesap silme işlemini yapabilmek için lütfen kimliğinizi doğrulayın'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Hesap silme işlemini onayla'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Kullandığınız Ente uygulamaları varsa bu hesap diğer Ente uygulamalarıyla bağlantılıdır.\n\nTüm Ente uygulamalarına yüklediğiniz veriler ve hesabınız kalıcı olarak silinecektir.'; @override - String get delete => 'Delete'; + String get delete => 'Sil'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Yeni hesap oluşturun'; @override - String get password => 'Password'; + String get password => 'Şifre'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Şifreyi onayla'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Şifre gücü: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ente\'yi nereden duydunuz? (opsiyonel)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Biz uygulama kurulumlarını takip etmiyoruz. Bizi nereden duyduğunuzdan bahsetmeniz bize çok yardımcı olacak!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Kullanım şartlarını ve gizlilik politikasını kabul ediyorum'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Şartlar'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Gizlilik Politikası'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Eğer şifremi kaybedersem, verilerim uçtan uca şifrelendiğinden verilerimi kaybedebileceğimi anladım.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Şifreleme'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Giriş yapın'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Tekrar hoş geldiniz!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Giriş yaparak, kullanım şartları nı ve gizlilik politikası nı onaylıyorum'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'İnternet bağlantısı yok'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Lütfen internet bağlantınızı kontrol edin ve yeniden deneyin.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Doğrulama başarısız oldu, lütfen tekrar deneyin'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Şifreyi yeniden oluştur'; @override String get recreatePasswordBody => - '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).'; + 'Mevcut cihaz şifrenizi doğrulayacak kadar güçlü değil, ancak tüm cihazlarla çalışacak şekilde yeniden oluşturabiliriz.\n\nLütfen kurtarma anahtarınızı kullanarak giriş yapın ve şifrenizi yeniden oluşturun (isterseniz aynı şifreyi tekrar kullanabilirsiniz).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Kurtarma anahtarını kullan'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Şifremi unuttum'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'E-posta adresini değiştir'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'E-posta adresini doğrulayın'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '$email adresine bir posta gönderdik'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Şifrenizi sıfırlamak için lütfen önce e-postanızı doğrulayın.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Doğrulamayı tamamlamak için lütfen gelen kutunuzu (ve spam kutunuzu) kontrol edin'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Kodu girmek için dokunun'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'E-posta gönder'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'E-postayı yeniden gönder'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Doğrulama hala bekliyor'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Oturum süresi doldu'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Oturum süreniz doldu. Tekrar giriş yapın.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Geçiş anahtarı doğrulaması'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Doğrulama bekleniyor...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Tekrar deneyin'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Durumu kontrol et'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'TOTP ile giriş yap'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Hesap kurtarma'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Şifre belirleyin'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Şifreyi değiştirin'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Şifreyi sıfırlayın'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Şifreleme anahtarları'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Verilerinizi şifrelemek için kullanabileceğimiz bir şifre girin'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Verilerinizi şifrelemek için kullanabileceğimiz yeni bir şifre girin'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Bu şifreyi saklamıyoruz, bu nedenle unutursanız, verilerinizin şifresini çözemeyiz'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Nasıl çalışır'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Şifreleme anahtarları oluşturuluyor...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Şifre başarıyla değiştirildi'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Diğer cihazlardan çıkış yap'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Eğer başka birisinin parolanızı bildiğini düşünüyorsanız, diğer tüm cihazları hesabınızdan çıkışa zorlayabilirsiniz.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Diğer cihazlardan çıkış yap'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Çıkış yapma'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Şifreleme anahtarları üretiliyor...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Devam et'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Güvenli olmayan cihaz'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Üzgünüz, bu cihazda güvenli anahtarlar oluşturamadık.\n\nlütfen farklı bir cihazdan kaydolun.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Kurtarma anahtarı panoya kopyalandı'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kurtarma Anahtarı'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Eğer şifrenizi unutursanız, verilerinizi kurtarabileceğiniz tek yol bu anahtardır.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Biz bu anahtarı saklamıyoruz, lütfen. bu 24 kelimelik anahtarı güvenli bir yerde saklayın.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Bunu daha sonra yap'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Anahtarı kaydet'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kurtarma anahtarı İndirilenler klasörüne kaydedildi!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Kurtarma anahtarınız yok mu?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'İki faktörlü kimlik doğrulama'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Kimlik doğrulayıcı uygulamanızdaki 6 haneli doğrulama kodunu girin'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Cihazınızı mı kaybettiniz?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Kurtarma anahtarınızı girin'; @override - String get recover => 'Recover'; + String get recover => 'Kurtar'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Çıkış yapılıyor...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Hemen'; @override - String get appLock => 'App lock'; + String get appLock => 'Uygulama kilidi'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Otomatik Kilit'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Sistem kilidi bulunamadı'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Cihaz kilidini etkinleştirmek için, lütfen cihaz şifresini veya ekran kilidini ayarlayın.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Cihazınızın varsayılan kilit ekranı ile PIN veya parola içeren özel bir kilit ekranı arasında seçim yapın.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Cihaz kilidi'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pin kilidi'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Uygulamayı arka plana attıktan sonra kilitlendiği süre'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'İçeriği gizle'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Uygulama değiştiricide bulunan uygulama içeriğini gizler ve ekran görüntülerini devre dışı bırakır'; @override String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + 'Uygulama değiştiricideki uygulama içeriğini gizler'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Çok fazla hatalı deneme'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Açmak için dokun'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => + 'Çıkış yapmak istediğinize emin misiniz?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Evet, çıkış yap'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => + 'Kodlarınızı görmek için lütfen kimlik doğrulaması yapın'; @override - String get next => 'Next'; + String get next => 'Sonraki'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Yeni şifre belirle'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'PIN Girin'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Yeni PIN belirleyin'; @override - String get confirm => 'Confirm'; + String get confirm => 'Doğrula'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Şifrenizi tekrar girin'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'PIN\'inizi tekrar girin'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Kimliği doğrula'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => 'Tanınmadı. Tekrar deneyin.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Başarılı'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'İptal et'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Kimlik doğrulaması gerekli'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Biyometrik gerekli'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Cihaz kimlik bilgileri gerekli'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Cihaz kimlik bilgileri gerekmekte'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Ayarlara git'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Biyometrik kimlik doğrulama cihazınızda ayarlanmamış. Biyometrik kimlik doğrulama eklemek için \'Ayarlar > Güvenlik\' bölümüne gidin.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Biyometrik kimlik doğrulama devre dışı. Etkinleştirmek için lütfen ekranınızı kilitleyin ve kilidini açın.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Tamam'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'E-posta zaten kayıtlı.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'E-posta kayıtlı değil.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Bu e-posta zaten kullanılıyor'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'E-posta $newEmail olarak değiştirildi'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Kimlik doğrulama başarısız oldu, lütfen tekrar deneyin'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Kimlik doğrulama başarılı!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Oturum süresi doldu'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Yanlış kurtarma kodu'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Girdiğiniz kurtarma kodu yanlış'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'İki faktörlü kimlik doğrulama başarıyla sıfırlandı'; @override String get noRecoveryKey => 'No recovery key'; @@ -611,12 +605,28 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + 'Doğrulama kodunuzun süresi doldu'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Yanlış kod'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Üzgünüz, girdiğiniz kod yanlış'; + + @override + String get developerSettings => 'Geliştirici ayarları'; + + @override + String get serverEndpoint => 'Sunucu uç noktası'; + + @override + String get invalidEndpoint => 'Geçersiz uç nokta'; + + @override + String get invalidEndpointMessage => + 'Üzgünüz, girdiğiniz uç nokta geçersiz. Lütfen geçerli bir uç nokta girin ve tekrar deneyin.'; + + @override + String get endpointUpdatedMessage => 'Uç nokta başarıyla güncellendi'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_uk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_uk.dart new file mode 100644 index 0000000000..31acfef908 --- /dev/null +++ b/mobile/packages/strings/lib/l10n/strings_localizations_uk.dart @@ -0,0 +1,634 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'strings_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Ukrainian (`uk`). +class StringsLocalizationsUk extends StringsLocalizations { + StringsLocalizationsUk([String locale = 'uk']) : super(locale); + + @override + String get networkHostLookUpErr => + 'Не вдалося приєднатися до Ente. Будь ласка, перевірте налаштування мережі. Зверніться до нашої команди підтримки, якщо помилка залишиться.'; + + @override + String get networkConnectionRefusedErr => + 'Не вдалося приєднатися до Ente. Будь ласка, спробуйте ще раз через деякий час. Якщо помилка не зникне, зв\'яжіться з нашою командою підтримки.'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + 'Схоже, що щось пішло не так. Будь ласка, спробуйте ще раз через деякий час. Якщо помилка не зникне, зв\'яжіться з нашою командою підтримки.'; + + @override + String get error => 'Помилка'; + + @override + String get ok => 'Ок'; + + @override + String get faq => 'Часті питання'; + + @override + String get contactSupport => 'Звернутися до служби підтримки'; + + @override + String get emailYourLogs => 'Відправте ваші журнали електронною поштою'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return 'Будь ласка, надішліть журнали до електронної пошти $toEmail'; + } + + @override + String get copyEmailAddress => 'Копіювати електронну адресу'; + + @override + String get exportLogs => 'Експортувати журнал'; + + @override + String get cancel => 'Скасувати'; + + @override + String pleaseEmailUsAt(String toEmail) { + return 'Email us at $toEmail'; + } + + @override + String get emailAddressCopied => 'Email address copied'; + + @override + String get supportEmailSubject => '[Support]'; + + @override + String get clientDebugInfoLabel => + 'Following information can help us in debugging if you are facing any issue'; + + @override + String get registeredEmailLabel => 'Registered email:'; + + @override + String get clientLabel => 'Client:'; + + @override + String get versionLabel => 'Version :'; + + @override + String get notAvailable => 'N/A'; + + @override + String get reportABug => 'Повідомити про помилку'; + + @override + String get logsDialogBody => + '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.'; + + @override + String get viewLogs => 'View logs'; + + @override + String customEndpoint(String endpoint) { + return 'Приєднано до $endpoint'; + } + + @override + String get save => 'Зберегти'; + + @override + String get send => 'Надіслати'; + + @override + String get saveOrSendDescription => + 'Чи хочете ви зберегти це до свого сховища (типово тека Downloads), чи надіслати його в інші застосунки?'; + + @override + String get saveOnlyDescription => + 'Чи хочете Ви зберегти це до свого сховища (тека Downloads за замовчуванням)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Адреса електронної пошти'; + + @override + String get verify => 'Перевірити'; + + @override + String get invalidEmailTitle => 'Хибна адреса електронної пошти'; + + @override + String get invalidEmailMessage => 'Введіть дійсну адресу електронної пошти.'; + + @override + String get pleaseWait => 'Будь ласка, зачекайте...'; + + @override + String get verifyPassword => 'Підтвердження пароля'; + + @override + String get incorrectPasswordTitle => 'Невірний пароль'; + + @override + String get pleaseTryAgain => 'Будь ласка, спробуйте ще раз'; + + @override + String get enterPassword => 'Введіть пароль'; + + @override + String get enterYourPasswordHint => 'Введіть свій пароль'; + + @override + String get activeSessions => 'Активні сеанси'; + + @override + String get oops => 'От халепа'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Щось пішло не так, спробуйте, будь ласка, знову'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'Це призведе до виходу на цьому пристрої!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'Це призведе до виходу на наступному пристрої:'; + + @override + String get terminateSession => 'Припинити сеанс?'; + + @override + String get terminate => 'Припинити'; + + @override + String get thisDevice => 'Цей пристрій'; + + @override + String get createAccount => 'Створити обліковий запис'; + + @override + String get weakStrength => 'Слабкий'; + + @override + String get moderateStrength => 'Помірний'; + + @override + String get strongStrength => 'Надійний'; + + @override + String get deleteAccount => 'Видалити обліковий запис'; + + @override + String get deleteAccountQuery => + 'Нам дуже шкода, що Ви залишаєте нас. Чи Ви зіткнулися з якоюсь проблемою?'; + + @override + String get yesSendFeedbackAction => 'Так, надіслати відгук'; + + @override + String get noDeleteAccountAction => 'Ні, видаліть мій обліковий запис'; + + @override + String get initiateAccountDeleteTitle => + 'Будь ласка, авторизуйтесь, щоб розпочати видалення облікового запису'; + + @override + String get confirmAccountDeleteTitle => + 'Підтвердіть видалення облікового запису'; + + @override + String get confirmAccountDeleteMessage => + 'Цей обліковий запис є зв\'язаним з іншими програмами Ente, якщо ви використовуєте якісь з них.\n\nВаші завантажені дані у всіх програмах Ente будуть заплановані до видалення, а обліковий запис буде видалено назавжди.'; + + @override + String get delete => 'Видалити'; + + @override + String get createNewAccount => 'Створити новий обліковий запис'; + + @override + String get password => 'Пароль'; + + @override + String get confirmPassword => 'Підтвердити пароль'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Сила пароля: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'Як ви дізналися про Ente? (опціонально)'; + + @override + String get hearUsExplanation => + 'Ми не відстежуємо встановлення застосунків. Але, якщо ви скажете нам, де ви нас знайшли, це допоможе!'; + + @override + String get signUpTerms => + 'Я приймаю умови використання і політику конфіденційності'; + + @override + String get termsOfServicesTitle => 'Умови'; + + @override + String get privacyPolicyTitle => 'Політика конфіденційності'; + + @override + String get ackPasswordLostWarning => + 'Я розумію, що якщо я втрачу свій пароль, я можу втратити свої дані, тому що вони є захищені наскрізним шифруванням.'; + + @override + String get encryption => 'Шифрування'; + + @override + String get logInLabel => 'Увійти'; + + @override + String get welcomeBack => 'З поверненням!'; + + @override + String get loginTerms => + 'Натискаючи «Увійти», я приймаю умови використання і політику конфіденційності'; + + @override + String get noInternetConnection => 'Немає підключення до Інтернету'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Будь ласка, перевірте підключення до Інтернету та спробуйте ще раз.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Перевірка не вдалася, спробуйте ще'; + + @override + String get recreatePasswordTitle => 'Повторно створити пароль'; + + @override + String get recreatePasswordBody => + 'Поточний пристрій не є достатньо потужним для підтвердження пароля, але ми можемо відновити його таким чином, щоб він працював на всіх пристроях.\n\nБудь ласка, увійдіть за допомогою вашого ключа відновлення і відновіть ваш пароль (ви можете знову використати той самий пароль, якщо бажаєте).'; + + @override + String get useRecoveryKey => 'Застосувати ключ відновлення'; + + @override + String get forgotPassword => 'Нагадати пароль'; + + @override + String get changeEmail => 'Змінити адресу електронної пошти'; + + @override + String get verifyEmail => 'Підтвердити електронну адресу'; + + @override + String weHaveSendEmailTo(String email) { + return 'Ми надіслали листа на адресу електронної пошти $email'; + } + + @override + String get toResetVerifyEmail => + 'Щоб скинути пароль, будь ласка, спочатку підтвердіть адресу своєї електронної пошти.'; + + @override + String get checkInboxAndSpamFolder => + 'Будь ласка, перевірте вашу скриньку електронної пошти (та спам), щоб завершити перевірку'; + + @override + String get tapToEnterCode => 'Натисніть, щоб ввести код'; + + @override + String get sendEmail => 'Надіслати електронного листа'; + + @override + String get resendEmail => 'Повторно надіслати лист на електронну пошту'; + + @override + String get passKeyPendingVerification => 'Підтвердження все ще в процесі'; + + @override + String get loginSessionExpired => 'Час сеансу минув'; + + @override + String get loginSessionExpiredDetails => + 'Термін дії вашого сеансу завершився. Будь ласка, увійдіть знову.'; + + @override + String get passkeyAuthTitle => 'Перевірка секретного ключа'; + + @override + String get waitingForVerification => 'Очікується підтвердження...'; + + @override + String get tryAgain => 'Спробуйте ще раз'; + + @override + String get checkStatus => 'Перевірити стан'; + + @override + String get loginWithTOTP => 'Увійти за допомогою TOTP'; + + @override + String get recoverAccount => 'Відновити обліковий запис'; + + @override + String get setPasswordTitle => 'Встановити пароль'; + + @override + String get changePasswordTitle => 'Змінити пароль'; + + @override + String get resetPasswordTitle => 'Скинути пароль'; + + @override + String get encryptionKeys => 'Ключі шифрування'; + + @override + String get enterPasswordToEncrypt => + 'Введіть пароль, який ми зможемо використати для шифрування ваших даних'; + + @override + String get enterNewPasswordToEncrypt => + 'Введіть новий пароль, який ми зможемо використати для шифрування ваших даних'; + + @override + String get passwordWarning => + 'Ми не зберігаємо цей пароль, тому, якщо ви його забудете, ми не зможемо розшифрувати Ваші дані'; + + @override + String get howItWorks => 'Як це працює'; + + @override + String get generatingEncryptionKeys => 'Створення ключів шифрування...'; + + @override + String get passwordChangedSuccessfully => 'Пароль успішно змінено'; + + @override + String get signOutFromOtherDevices => 'Вийти на інших пристроях'; + + @override + String get signOutOtherBody => + 'Якщо ви думаєте, що хтось може знати ваш пароль, ви можете примусити всі інші пристрої, які використовують ваш обліковий запис, вийти з нього.'; + + @override + String get signOutOtherDevices => 'Вийти на інших пристроях'; + + @override + String get doNotSignOut => 'Не виходити'; + + @override + String get generatingEncryptionKeysTitle => 'Створення ключів шифрування...'; + + @override + String get continueLabel => 'Продовжити'; + + @override + String get insecureDevice => 'Незахищений пристрій'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'На жаль, нам не вдалося згенерувати захищені ключі на цьому пристрої.\n\nБудь ласка, увійдіть з іншого пристрою.'; + + @override + String get recoveryKeyCopiedToClipboard => + 'Ключ відновлення скопійований в буфер обміну'; + + @override + String get recoveryKey => 'Ключ відновлення'; + + @override + String get recoveryKeyOnForgotPassword => + 'Якщо ви забудете свій пароль, то єдиний спосіб відновити ваші дані – за допомогою цього ключа.'; + + @override + String get recoveryKeySaveDescription => + 'Ми не зберігаємо цей ключ, будь ласка, збережіть цей ключ з 24 слів в надійному місці.'; + + @override + String get doThisLater => 'Зробити це пізніше'; + + @override + String get saveKey => 'Зберегти ключ'; + + @override + String get recoveryKeySaved => + 'Ключ відновлення збережений у теці Downloads!'; + + @override + String get noRecoveryKeyTitle => 'Немає ключа відновлення?'; + + @override + String get twoFactorAuthTitle => 'Двоетапна автентифікація'; + + @override + String get enterCodeHint => + 'Введіть нижче шестизначний код із застосунку для автентифікації'; + + @override + String get lostDeviceTitle => 'Загубили пристрій?'; + + @override + String get enterRecoveryKeyHint => 'Введіть ваш ключ відновлення'; + + @override + String get recover => 'Відновлення'; + + @override + String get loggingOut => 'Вихід із системи...'; + + @override + String get immediately => 'Негайно'; + + @override + String get appLock => 'Блокування'; + + @override + String get autoLock => 'Автоблокування'; + + @override + String get noSystemLockFound => 'Не знайдено системного блокування'; + + @override + String get deviceLockEnablePreSteps => + 'Для увімкнення блокування програми, будь ласка, налаштуйте пароль пристрою або блокування екрана в системних налаштуваннях.'; + + @override + String get appLockDescription => + 'Виберіть між типовим екраном блокування вашого пристрою та власним екраном блокування з PIN-кодом або паролем.'; + + @override + String get deviceLock => 'Блокування пристрою'; + + @override + String get pinLock => 'PIN-код'; + + @override + String get autoLockFeatureDescription => + 'Час, через який застосунок буде заблоковано після розміщення у фоновому режимі'; + + @override + String get hideContent => 'Приховати вміст'; + + @override + String get hideContentDescriptionAndroid => + 'Приховує вміст програми у перемикачі застосунків і вимикає знімки екрану'; + + @override + String get hideContentDescriptioniOS => + 'Приховує вміст у перемикачі застосунків'; + + @override + String get tooManyIncorrectAttempts => 'Завелика кількість невірних спроб'; + + @override + String get tapToUnlock => 'Доторкніться, щоб розблокувати'; + + @override + String get areYouSureYouWantToLogout => + 'Ви впевнені, що хочете вийти з системи?'; + + @override + String get yesLogout => 'Так, вийти з системи'; + + @override + String get authToViewSecrets => + 'Будь ласка, пройдіть автентифікацію, щоб переглянути ваші секретні коди'; + + @override + String get next => 'Наступний'; + + @override + String get setNewPassword => 'Встановити новий пароль'; + + @override + String get enterPin => 'Введіть PIN-код'; + + @override + String get setNewPin => 'Встановити новий PIN-код'; + + @override + String get confirm => 'Підтвердити'; + + @override + String get reEnterPassword => 'Введіть пароль ще раз'; + + @override + String get reEnterPin => 'Введіть PIN-код ще раз'; + + @override + String get androidBiometricHint => 'Підтвердити ідентифікацію'; + + @override + String get androidBiometricNotRecognized => + 'Не розпізнано. Спробуйте ще раз.'; + + @override + String get androidBiometricSuccess => 'Успіх'; + + @override + String get androidCancelButton => 'Скасувати'; + + @override + String get androidSignInTitle => 'Необхідна автентифікація'; + + @override + String get androidBiometricRequiredTitle => + 'Потрібна біометрична автентифікація'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Необхідні облікові дані пристрою'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Необхідні облікові дані пристрою'; + + @override + String get goToSettings => 'Перейти до налаштувань'; + + @override + String get androidGoToSettingsDescription => + 'Біометрична автентифікація не налаштована на вашому пристрої. Перейдіть в «Налаштування > Безпека», щоб додати біометричну автентифікацію.'; + + @override + String get iOSLockOut => + 'Біометрична автентифікація вимкнена. Будь ласка, заблокуйте і розблокуйте свій екран, щоб увімкнути її.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Ця адреса електронної пошти вже використовується'; + + @override + String emailChangedTo(String newEmail) { + return 'Адресу електронної пошти змінено на $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Автентифікація не пройдена. Будь ласка, спробуйте ще раз'; + + @override + String get authenticationSuccessful => 'Автентифікацію виконано!'; + + @override + String get sessionExpired => 'Час сеансу минув'; + + @override + String get incorrectRecoveryKey => 'Неправильний ключ відновлення'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Ви ввели неправильний ключ відновлення'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Двоетапна автентифікація успішно скинута'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Час дії коду підтвердження минув'; + + @override + String get incorrectCode => 'Невірний код'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Вибачте, але введений вами код є невірним'; + + @override + String get developerSettings => 'Налаштування розробника'; + + @override + String get serverEndpoint => 'Кінцева точка сервера'; + + @override + String get invalidEndpoint => 'Некоректна кінцева точка'; + + @override + String get invalidEndpointMessage => + 'Вибачте, введена вами кінцева точка є недійсною. Введіть дійсну кінцеву точку та спробуйте ще раз.'; + + @override + String get endpointUpdatedMessage => 'Точка входу успішно оновлена'; +} diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index 6cb43475bc..a55e1a242c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -14,40 +14,40 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + 'Không thể kết nối đến Ente, vui lòng thử lại sau. Nếu vẫn còn lỗi, xin vui lòng liên hệ hỗ trợ.'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + 'Có vẻ như đã xảy ra sự cố. Vui lòng thử lại sau một thời gian. Nếu lỗi vẫn tiếp diễn, vui lòng liên hệ với nhóm hỗ trợ của chúng tôi.'; @override - String get error => 'Error'; + String get error => 'Lỗi'; @override - String get ok => 'Ok'; + String get ok => 'Đồng ý'; @override - String get faq => 'FAQ'; + String get faq => 'Câu hỏi thường gặp'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => 'Liên hệ hỗ trợ'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => 'Gửi email nhật ký của bạn'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return 'Vui lòng gửi nhật ký đến \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => 'Sao chép địa chỉ email'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => 'Xuất nhật ký'; @override - String get cancel => 'Cancel'; + String get cancel => 'Hủy'; @override String pleaseEmailUsAt(String toEmail) { @@ -77,19 +77,7 @@ class StringsLocalizationsVi extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => 'Báo cáo lỗi'; @override String get logsDialogBody => @@ -100,505 +88,508 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return 'Đã kết nối đến'; } @override - String get save => 'Save'; + String get save => 'Lưu'; @override - String get send => 'Send'; + String get send => 'Gửi'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + 'Bạn có muốn lưu vào bộ nhớ (Mặc định lưu vào thư mục Tải về) hoặc chuyển qua ứng dụng khác?'; @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Bạn có muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Thư điện tử'; @override - String get verify => 'Verify'; + String get verify => 'Xác minh'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Địa chỉ email không hợp lệ'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Xin vui lòng nhập một địa chỉ email hợp lệ.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vui lòng chờ...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Xác nhận mật khẩu'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Mật khẩu không đúng'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Vui lòng thử lại'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Nhập mật khẩu'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Nhập mật khẩu của bạn'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Các phiên làm việc hiện tại'; @override - String get oops => 'Oops'; + String get oops => 'Rất tiếc'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Phát hiện có lỗi, xin thử lại'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị này!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị sau:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Kết thúc phiên?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Kết thúc'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Thiết bị này'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Tạo tài khoản'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Yếu'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Trung bình'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Mạnh'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Xoá tài khoản'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Chúng tôi sẽ rất tiếc khi thấy bạn đi. Bạn đang phải đối mặt với một số vấn đề?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Có, gửi phản hồi'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Không, xóa tài khoản'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Vui lòng xác thực để bắt đầu xóa tài khoản'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Xác nhận xóa tài khoản'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.'; @override - String get delete => 'Delete'; + String get delete => 'Xóa'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Tạo tài khoản mới'; @override - String get password => 'Password'; + String get password => 'Mật khẩu'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Xác nhận mật khẩu'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Độ mạnh mật khẩu: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => + 'Bạn biết đến Ente bằng cách nào? (không bắt buộc)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Điều khoản'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Chính sách bảo mật'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Tôi hiểu rằng việc mất mật khẩu có thể đồng nghĩa với việc mất dữ liệu của tôi vì dữ liệu của tôi được mã hóa hai đầu.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Mã hóa'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Đăng nhập'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Chào mừng trở lại!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Bằng cách nhấp vào đăng nhập, tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Không có kết nối Internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Vui lòng kiểm tra kết nối internet của bạn và thử lại.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Mã xác nhận thất bại. Vui lòng thử lại'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Tạo lại mật khẩu'; @override String get recreatePasswordBody => - '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).'; + 'Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn nhưng chúng tôi có thể tạo lại mật khẩu theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dụng lại cùng một mật khẩu nếu muốn).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Dùng khóa khôi phục'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Quên mật khẩu'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Thay đổi email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Xác nhận địa chỉ Email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Chúng tôi đã gửi thư đến $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Để đặt lại mật khẩu, vui lòng xác minh email của bạn trước.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Vui lòng kiểm tra hộp thư đến (và thư rác) của bạn để hoàn tất xác minh'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Chạm để nhập mã'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Gửi email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Gửi lại email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Đang chờ xác thực'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Phiên làm việc hết hạn'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Phiên làm việc hết hạn. Vui lòng đăng nhập lại.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Xác minh mã khóa'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Đang chờ xác thực'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Thử lại'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Kiểm tra trạng thái'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Đăng nhập bằng TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Khôi phục tài khoản'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Đặt mật khẩu'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Thay đổi mật khẩu'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Đặt lại mật khẩu'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Khóa mã hóa'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Nhập mật khẩu mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Nhập một mật khẩu mới mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Chúng tôi không lưu trữ mật khẩu này, vì vậy nếu bạn quên, chúng tôi không thể giải mã dữ liệu của bạn'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cách thức hoạt động'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Đang tạo khóa mã hóa...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Thay đổi mật khẩu thành công'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Nếu bạn cho rằng ai đó có thể biết mật khẩu của mình, bạn có thể buộc đăng xuất tất cả các thiết bị khác đang sử dụng tài khoản của mình.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Không được đăng xuất'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Đang tạo khóa mã hóa...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Tiếp tục'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Thiết bị không an toàn'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Rất tiếc, chúng tôi không thể tạo khóa bảo mật trên thiết bị này.\n\nvui lòng đăng ký từ một thiết bị khác.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Đã sao chép khóa khôi phục vào bộ nhớ tạm'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Khóa khôi phục'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Nếu bạn quên mật khẩu, cách duy nhất bạn có thể khôi phục dữ liệu của mình là sử dụng khóa này.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Để sau'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Lưu khóa'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => 'Đã lưu khoá dự phòng vào thư mục Tải về!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Không có khóa khôi phục?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Xác thực hai yếu tố'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Mất thiết bị?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Nhập khóa khôi phục của bạn'; @override - String get recover => 'Recover'; + String get recover => 'Khôi phục'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => 'Đang đăng xuất...'; @override - String get immediately => 'Immediately'; + String get immediately => 'Tức thì'; @override - String get appLock => 'App lock'; + String get appLock => 'Khóa ứng dụng'; @override - String get autoLock => 'Auto lock'; + String get autoLock => 'Tự động khóa'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => 'Không thấy khoá hệ thống'; @override String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + 'Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.'; @override String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + 'Chọn giữa màn hình khoá mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => 'Khóa thiết bị'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Mã PIN'; @override String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + 'Thời gian ứng dụng tự khoá sau khi ở trạng thái nền'; @override - String get hideContent => 'Hide content'; + String get hideContent => 'Ẩn nội dung'; @override String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + 'Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => 'Ẩn nội dung khi chuyển ứng dụng'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => 'Quá nhiều lần thử không chính xác'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => 'Nhấn để mở khóa'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => 'Bạn có chắc chắn muốn đăng xuất?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => 'Có, đăng xuất'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => 'Vui lòng xác thực để xem bí mật của bạn'; @override - String get next => 'Next'; + String get next => 'Tiếp theo'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => 'Đặt lại mật khẩu'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => 'Nhập mã PIN'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => 'Đổi mã PIN'; @override - String get confirm => 'Confirm'; + String get confirm => 'Xác nhận'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => 'Nhập lại mật khẩu'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => 'Nhập lại mã PIN'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => 'Xác định danh tính'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => + 'Không nhận dạng được. Vui lòng thử lại.'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => 'Thành công'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => 'Hủy bỏ'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => 'Yêu cầu xác thực'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => 'Yêu cầu sinh trắc học'; @override String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + 'Yêu cầu thông tin xác thực thiết bị'; @override String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + 'Yêu cầu thông tin xác thực thiết bị'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => 'Chuyển đến cài đặt'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + 'Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi tới \'Cài đặt > Bảo mật\' để thêm xác thực sinh trắc học.'; @override String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + 'Xác thực sinh trắc học bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt nó.'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => 'Đồng ý'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => 'Email đã được đăng kí.'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => 'Email chưa được đăng kí.'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => 'Email này đã được sử dụng'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return 'Thay đổi email thành $newEmail'; } @override String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + 'Xác thực lỗi, vui lòng thử lại'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => 'Xác thực thành công!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => 'Phiên làm việc đã hết hạn'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => 'Khóa khôi phục không chính xác'; @override String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + 'Khóa khôi phục bạn đã nhập không chính xác'; @override String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + 'Xác thực hai bước được khôi phục thành công'; @override String get noRecoveryKey => 'No recovery key'; @@ -610,13 +601,28 @@ class StringsLocalizationsVi extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => 'Mã xác minh của bạn đã hết hạn'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => 'Mã không chính xác'; @override String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + 'Xin lỗi, mã bạn đã nhập không chính xác'; + + @override + String get developerSettings => 'Cài đặt cho nhà phát triển'; + + @override + String get serverEndpoint => 'Điểm cuối máy chủ'; + + @override + String get invalidEndpoint => 'Điểm cuối không hợp lệ'; + + @override + String get invalidEndpointMessage => + 'Xin lỗi, điểm cuối bạn nhập không hợp lệ. Vui lòng nhập một điểm cuối hợp lệ và thử lại.'; + + @override + String get endpointUpdatedMessage => 'Cập nhật điểm cuối thành công'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index ef097b2199..d0447c1877 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -9,44 +9,44 @@ class StringsLocalizationsZh extends StringsLocalizations { StringsLocalizationsZh([String locale = 'zh']) : super(locale); @override - String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存在,请联系支持。'; + String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。'; @override String get networkConnectionRefusedErr => - 'Unable to connect to Ente, please retry after sometime. If the error persists, please contact support.'; + '无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。'; @override String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - 'It looks like something went wrong. Please retry after some time. If the error persists, please contact our support team.'; + '看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。'; @override - String get error => 'Error'; + String get error => '错误'; @override - String get ok => 'Ok'; + String get ok => '确定'; @override - String get faq => 'FAQ'; + String get faq => '常见问题'; @override - String get contactSupport => 'Contact support'; + String get contactSupport => '联系支持'; @override - String get emailYourLogs => 'Email your logs'; + String get emailYourLogs => '通过电子邮件发送您的日志'; @override String pleaseSendTheLogsTo(String toEmail) { - return 'Please send the logs to \n$toEmail'; + return '请将日志发送至 \n$toEmail'; } @override - String get copyEmailAddress => 'Copy email address'; + String get copyEmailAddress => '复制电子邮件地址'; @override - String get exportLogs => 'Export logs'; + String get exportLogs => '导出日志'; @override - String get cancel => 'Cancel'; + String get cancel => '取消'; @override String pleaseEmailUsAt(String toEmail) { @@ -76,19 +76,7 @@ class StringsLocalizationsZh extends StringsLocalizations { String get notAvailable => 'N/A'; @override - String get enteLogsPrefix => 'ente-logs-'; - - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; + String get reportABug => '报告错误'; @override String get logsDialogBody => @@ -99,505 +87,476 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return '已连接至 $endpoint'; } @override - String get save => 'Save'; + String get save => '保存'; @override - String get send => 'Send'; + String get send => '发送'; @override String get saveOrSendDescription => - 'Do you want to save this to your storage (Downloads folder by default) or send it to other apps?'; + '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; @override - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => '电子邮件地址'; @override - String get verify => 'Verify'; + String get verify => '验证'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => '无效的电子邮件地址'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => '请稍候...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => '验证密码'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => '密码错误'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => '请重试'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => '输入密码'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => '输入您的密码'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => '已登录的设备'; @override - String get oops => 'Oops'; + String get oops => '哎呀'; @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => '是否终止会话?'; @override - String get terminate => 'Terminate'; + String get terminate => '终止'; @override - String get thisDevice => 'This device'; + String get thisDevice => '此设备'; @override - String get createAccount => 'Create account'; + String get createAccount => '创建账户'; @override - String get weakStrength => 'Weak'; + String get weakStrength => '弱'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => '中'; @override - String get strongStrength => 'Strong'; + String get strongStrength => '强'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => '删除账户'; @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => '是,发送反馈'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => '否,删除账户'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => '确认删除账户'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; @override - String get delete => 'Delete'; + String get delete => '删除'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => '创建新账号'; @override - String get password => 'Password'; + String get password => '密码'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => '请确认密码'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return '密码强度: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + '我同意 服务条款隐私政策'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => '服务条款'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => '隐私政策'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; @override - String get encryption => 'Encryption'; + String get encryption => '加密'; @override - String get logInLabel => 'Log in'; + String get logInLabel => '登录'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '欢迎回来!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + '点击登录后,我同意 服务条款隐私政策'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => '无互联网连接'; @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => '重新创建密码'; @override String get recreatePasswordBody => - '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).'; + '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => '使用恢复密钥'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => '忘记密码'; @override - String get changeEmail => 'Change email'; + String get changeEmail => '修改邮箱'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => '验证电子邮件'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '我们已经发送邮件到 $email'; } @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => '点击以输入代码'; @override - String get sendEmail => 'Send email'; + String get sendEmail => '发送电子邮件'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => '重新发送电子邮件'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => '仍需验证'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => '会话已过期'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => '通行密钥验证'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => '等待验证...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => '请再试一次'; @override - String get checkStatus => 'Check status'; + String get checkStatus => '检查状态'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => '使用 TOTP 登录'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => '恢复账户'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => '设置密码'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => '修改密码'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => '重置密码'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => '加密密钥'; @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; @override - String get howItWorks => 'How it works'; + String get howItWorks => '工作原理'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => '正在生成加密密钥...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => '密码修改成功'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => '从其他设备登出'; @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => '登出其他设备'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => '不要登出'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => '继续'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => '设备不安全'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => '恢复密钥'; @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => '稍后再做'; @override - String get saveKey => 'Save key'; + String get saveKey => '保存密钥'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '没有恢复密钥吗?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => '两步验证'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '丢失了设备吗?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => '输入您的恢复密钥'; @override - String get recover => 'Recover'; + String get recover => '恢复'; @override - String get loggingOut => 'Logging out...'; + String get loggingOut => '正在登出...'; @override - String get immediately => 'Immediately'; + String get immediately => '立即'; @override - String get appLock => 'App lock'; + String get appLock => '应用锁'; @override - String get autoLock => 'Auto lock'; + String get autoLock => '自动锁定'; @override - String get noSystemLockFound => 'No system lock found'; + String get noSystemLockFound => '未找到系统锁'; @override - String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; @override - String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; @override - String get deviceLock => 'Device lock'; + String get deviceLock => '设备锁'; @override - String get pinLock => 'Pin lock'; + String get pinLock => 'Pin 锁定'; @override - String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; + String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; @override - String get hideContent => 'Hide content'; + String get hideContent => '隐藏内容'; @override - String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; + String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; + String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + String get tooManyIncorrectAttempts => '错误的尝试次数过多'; @override - String get tapToUnlock => 'Tap to unlock'; + String get tapToUnlock => '点击解锁'; @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + String get areYouSureYouWantToLogout => '您确定要登出吗?'; @override - String get yesLogout => 'Yes, logout'; + String get yesLogout => '是的,登出'; @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; + String get authToViewSecrets => '请进行身份验证以查看您的密钥'; @override - String get next => 'Next'; + String get next => '下一步'; @override - String get setNewPassword => 'Set new password'; + String get setNewPassword => '设置新密码'; @override - String get enterPin => 'Enter PIN'; + String get enterPin => '输入 PIN 码'; @override - String get setNewPin => 'Set new PIN'; + String get setNewPin => '设置新 PIN 码'; @override - String get confirm => 'Confirm'; + String get confirm => '确认'; @override - String get reEnterPassword => 'Re-enter password'; + String get reEnterPassword => '再次输入密码'; @override - String get reEnterPin => 'Re-enter PIN'; + String get reEnterPin => '再次输入 PIN 码'; @override - String get androidBiometricHint => 'Verify identity'; + String get androidBiometricHint => '验证身份'; @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + String get androidBiometricNotRecognized => '未能识别,请重试。'; @override - String get androidBiometricSuccess => 'Success'; + String get androidBiometricSuccess => '成功'; @override - String get androidCancelButton => 'Cancel'; + String get androidCancelButton => '取消'; @override - String get androidSignInTitle => 'Authentication required'; + String get androidSignInTitle => '需要进行身份验证'; @override - String get androidBiometricRequiredTitle => 'Biometric required'; + String get androidBiometricRequiredTitle => '需要进行生物识别认证'; @override - String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; + String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; @override - String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; + String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; @override - String get goToSettings => 'Go to settings'; + String get goToSettings => '前往设置'; @override String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; @override - String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; @override - String get iOSOkButton => 'OK'; + String get iOSOkButton => '好'; @override - String get emailAlreadyRegistered => 'Email already registered.'; + String get emailAlreadyRegistered => '电子邮件地址已被注册。'; @override - String get emailNotRegistered => 'Email not registered.'; + String get emailNotRegistered => '电子邮件地址未注册。'; @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; + String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; @override String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; + return '电子邮件已更改为 $newEmail'; } @override - String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; + String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; @override - String get authenticationSuccessful => 'Authentication successful!'; + String get authenticationSuccessful => '认证成功!'; @override - String get sessionExpired => 'Session expired'; + String get sessionExpired => '会话已过期'; @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; + String get incorrectRecoveryKey => '恢复密钥不正确'; @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; + String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; @override - String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; + String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; @override String get noRecoveryKey => 'No recovery key'; @@ -609,343 +568,573 @@ class StringsLocalizationsZh extends StringsLocalizations { String get verificationId => 'Verification ID'; @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; + String get yourVerificationCodeHasExpired => '您的验证码已过期'; @override - String get incorrectCode => 'Incorrect code'; + String get incorrectCode => '验证码错误'; @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; + String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get developerSettings => '开发者设置'; @override - String get email => 'Email'; + String get serverEndpoint => '服务器端点'; @override - String get verify => 'Verify'; + String get invalidEndpoint => '端点无效'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get endpointUpdatedMessage => '端点更新成功'; +} + +/// The translations for Chinese, as used in China (`zh_CN`). +class StringsLocalizationsZhCn extends StringsLocalizationsZh { + StringsLocalizationsZhCn() : super('zh_CN'); @override - String get pleaseWait => 'Please wait...'; + String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。'; @override - String get verifyPassword => 'Verify password'; + String get networkConnectionRefusedErr => + '无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + '看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。'; @override - String get pleaseTryAgain => 'Please try again'; + String get error => '错误'; @override - String get enterPassword => 'Enter password'; + String get ok => '确定'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get faq => '常见问题'; @override - String get activeSessions => 'Active sessions'; + String get contactSupport => '联系支持'; @override - String get oops => 'Oops'; + String get emailYourLogs => '通过电子邮件发送您的日志'; @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + String pleaseSendTheLogsTo(String toEmail) { + return '请将日志发送至 \n$toEmail'; + } @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get copyEmailAddress => '复制电子邮件地址'; @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + String get exportLogs => '导出日志'; @override - String get terminateSession => 'Terminate session?'; + String get cancel => '取消'; @override - String get terminate => 'Terminate'; + String get reportABug => '报告错误'; @override - String get thisDevice => 'This device'; + String customEndpoint(String endpoint) { + return '已连接至 $endpoint'; + } @override - String get createAccount => 'Create account'; + String get save => '保存'; @override - String get weakStrength => 'Weak'; + String get send => '发送'; @override - String get moderateStrength => 'Moderate'; + String get saveOrSendDescription => + '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; @override - String get strongStrength => 'Strong'; + String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; @override - String get deleteAccount => 'Delete account'; + String get enterNewEmailHint => '请输入您的新电子邮件地址'; @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + String get email => '电子邮件地址'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get verify => '验证'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get invalidEmailTitle => '无效的电子邮件地址'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get pleaseWait => '请稍候...'; + + @override + String get verifyPassword => '验证密码'; + + @override + String get incorrectPasswordTitle => '密码错误'; + + @override + String get pleaseTryAgain => '请重试'; + + @override + String get enterPassword => '输入密码'; + + @override + String get enterYourPasswordHint => '输入您的密码'; + + @override + String get activeSessions => '已登录的设备'; + + @override + String get oops => '哎呀'; + + @override + String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; + + @override + String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; + + @override + String get terminateSession => '是否终止会话?'; + + @override + String get terminate => '终止'; + + @override + String get thisDevice => '此设备'; + + @override + String get createAccount => '创建账户'; + + @override + String get weakStrength => '弱'; + + @override + String get moderateStrength => '中'; + + @override + String get strongStrength => '强'; + + @override + String get deleteAccount => '删除账户'; + + @override + String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; + + @override + String get yesSendFeedbackAction => '是,发送反馈'; + + @override + String get noDeleteAccountAction => '否,删除账户'; + + @override + String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; + + @override + String get confirmAccountDeleteTitle => '确认删除账户'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; @override - String get delete => 'Delete'; + String get delete => '删除'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => '创建新账号'; @override - String get password => 'Password'; + String get password => '密码'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => '请确认密码'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return '密码强度: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + '我同意 服务条款隐私政策'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => '服务条款'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => '隐私政策'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; @override - String get encryption => 'Encryption'; + String get encryption => '加密'; @override - String get logInLabel => 'Log in'; + String get logInLabel => '登录'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '欢迎回来!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + '点击登录后,我同意 服务条款隐私政策'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => '无互联网连接'; @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => '重新创建密码'; @override String get recreatePasswordBody => - '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).'; + '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => '使用恢复密钥'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => '忘记密码'; @override - String get changeEmail => 'Change email'; + String get changeEmail => '修改邮箱'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => '验证电子邮件'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '我们已经发送邮件到 $email'; } @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => '点击以输入代码'; @override - String get sendEmail => 'Send email'; + String get sendEmail => '发送电子邮件'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => '重新发送电子邮件'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => '仍需验证'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => '会话已过期'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => '通行密钥验证'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => '等待验证...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => '请再试一次'; @override - String get checkStatus => 'Check status'; + String get checkStatus => '检查状态'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => '使用 TOTP 登录'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => '恢复账户'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => '设置密码'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => '修改密码'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => '重置密码'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => '加密密钥'; @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; @override - String get howItWorks => 'How it works'; + String get howItWorks => '工作原理'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => '正在生成加密密钥...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => '密码修改成功'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => '从其他设备登出'; @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => '登出其他设备'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => '不要登出'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => '继续'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => '设备不安全'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => '恢复密钥'; @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => '稍后再做'; @override - String get saveKey => 'Save key'; + String get saveKey => '保存密钥'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '没有恢复密钥吗?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => '两步验证'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '丢失了设备吗?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => '输入您的恢复密钥'; @override - String get recover => 'Recover'; + String get recover => '恢复'; + + @override + String get loggingOut => '正在登出...'; + + @override + String get immediately => '立即'; + + @override + String get appLock => '应用锁'; + + @override + String get autoLock => '自动锁定'; + + @override + String get noSystemLockFound => '未找到系统锁'; + + @override + String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; + + @override + String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; + + @override + String get deviceLock => '设备锁'; + + @override + String get pinLock => 'Pin 锁定'; + + @override + String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; + + @override + String get hideContent => '隐藏内容'; + + @override + String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; + + @override + String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; + + @override + String get tooManyIncorrectAttempts => '错误的尝试次数过多'; + + @override + String get tapToUnlock => '点击解锁'; + + @override + String get areYouSureYouWantToLogout => '您确定要登出吗?'; + + @override + String get yesLogout => '是的,登出'; + + @override + String get authToViewSecrets => '请进行身份验证以查看您的密钥'; + + @override + String get next => '下一步'; + + @override + String get setNewPassword => '设置新密码'; + + @override + String get enterPin => '输入 PIN 码'; + + @override + String get setNewPin => '设置新 PIN 码'; + + @override + String get confirm => '确认'; + + @override + String get reEnterPassword => '再次输入密码'; + + @override + String get reEnterPin => '再次输入 PIN 码'; + + @override + String get androidBiometricHint => '验证身份'; + + @override + String get androidBiometricNotRecognized => '未能识别,请重试。'; + + @override + String get androidBiometricSuccess => '成功'; + + @override + String get androidCancelButton => '取消'; + + @override + String get androidSignInTitle => '需要进行身份验证'; + + @override + String get androidBiometricRequiredTitle => '需要进行生物识别认证'; + + @override + String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; + + @override + String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; + + @override + String get goToSettings => '前往设置'; + + @override + String get androidGoToSettingsDescription => + '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; + + @override + String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; + + @override + String get iOSOkButton => '好'; + + @override + String get emailAlreadyRegistered => '电子邮件地址已被注册。'; + + @override + String get emailNotRegistered => '电子邮件地址未注册。'; + + @override + String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; + + @override + String emailChangedTo(String newEmail) { + return '电子邮件已更改为 $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; + + @override + String get authenticationSuccessful => '认证成功!'; + + @override + String get sessionExpired => '会话已过期'; + + @override + String get incorrectRecoveryKey => '恢复密钥不正确'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; + + @override + String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; + + @override + String get yourVerificationCodeHasExpired => '您的验证码已过期'; + + @override + String get incorrectCode => '验证码错误'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; + + @override + String get developerSettings => '开发者设置'; + + @override + String get serverEndpoint => '服务器端点'; + + @override + String get invalidEndpoint => '端点无效'; + + @override + String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; + + @override + String get endpointUpdatedMessage => '端点更新成功'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). @@ -953,5 +1142,543 @@ class StringsLocalizationsZhTw extends StringsLocalizationsZh { StringsLocalizationsZhTw() : super('zh_TW'); @override - String get networkHostLookUpErr => '無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存在,請聯絡支援。'; + String get networkHostLookUpErr => '無法連接到 Ente,請檢查您的網路設定,如果錯誤仍然存​​在,請聯絡支援。'; + + @override + String get networkConnectionRefusedErr => + '無法連接到 Ente,請稍後重試。如果錯誤仍然存​​在,請聯絡支援人員。'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + '看起來出了點問題。 請稍後重試。 如果錯誤仍然存在,請聯絡我們的支援團隊。'; + + @override + String get error => '錯誤'; + + @override + String get ok => '確定'; + + @override + String get faq => '常見問題'; + + @override + String get contactSupport => '聯絡支援'; + + @override + String get emailYourLogs => '通過電子郵件傳送您的日誌'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return '請將日誌傳送至 \n$toEmail'; + } + + @override + String get copyEmailAddress => '複製電子郵件地址'; + + @override + String get exportLogs => '匯出日誌'; + + @override + String get cancel => '取消'; + + @override + String get reportABug => '報告錯誤'; + + @override + String customEndpoint(String endpoint) { + return '已連接至 $endpoint'; + } + + @override + String get save => '儲存'; + + @override + String get send => '傳送'; + + @override + String get saveOrSendDescription => + '您想將其儲存到您的內建儲存(預設情況下為“下載”資料夾)還是將其傳送到其他APP?'; + + @override + String get saveOnlyDescription => '您想將其儲存到您的內建儲存中(預設情況下為“下載”資料夾)嗎?'; + + @override + String get enterNewEmailHint => '輸入你的新電子郵件地址'; + + @override + String get email => '電子郵件地址'; + + @override + String get verify => '驗證'; + + @override + String get invalidEmailTitle => '無效的電子郵件地址'; + + @override + String get invalidEmailMessage => '請輸入一個有效的電子郵件地址。'; + + @override + String get pleaseWait => '請稍候...'; + + @override + String get verifyPassword => '驗證密碼'; + + @override + String get incorrectPasswordTitle => '密碼錯誤'; + + @override + String get pleaseTryAgain => '請重試'; + + @override + String get enterPassword => '輸入密碼'; + + @override + String get enterYourPasswordHint => '輸入您的密碼'; + + @override + String get activeSessions => '已登錄的裝置'; + + @override + String get oops => '哎呀'; + + @override + String get somethingWentWrongPleaseTryAgain => '出了點問題,請重試'; + + @override + String get thisWillLogYouOutOfThisDevice => '這將使您登出該裝置!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => '這將使您登出以下裝置:'; + + @override + String get terminateSession => '是否終止工作階段?'; + + @override + String get terminate => '終止'; + + @override + String get thisDevice => '此裝置'; + + @override + String get createAccount => '建立帳戶'; + + @override + String get weakStrength => '弱'; + + @override + String get moderateStrength => '中'; + + @override + String get strongStrength => '強'; + + @override + String get deleteAccount => '刪除帳戶'; + + @override + String get deleteAccountQuery => '我們很抱歉看到您要刪除帳戶。您似乎面臨著一些問題?'; + + @override + String get yesSendFeedbackAction => '是,傳送回饋'; + + @override + String get noDeleteAccountAction => '否,刪除帳戶'; + + @override + String get initiateAccountDeleteTitle => '請進行身份驗證以啟動帳戶刪除'; + + @override + String get confirmAccountDeleteTitle => '確認刪除帳戶'; + + @override + String get confirmAccountDeleteMessage => + '如果您使用其他 Ente APP,該帳戶將會與其他APP連結。\n\n在所有 Ente APP中,您上傳的資料將被安排用於刪除,並且您的帳戶將被永久刪除。'; + + @override + String get delete => '刪除'; + + @override + String get createNewAccount => '建立新帳號'; + + @override + String get password => '密碼'; + + @override + String get confirmPassword => '請確認密碼'; + + @override + String passwordStrength(String passwordStrengthValue) { + return '密碼強度: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => '您是怎麼知道 Ente 的?(可選)'; + + @override + String get hearUsExplanation => '我們不跟蹤APP安裝情況。如果您告訴我們您是在哪裡找到我們的,將會有所幫助!'; + + @override + String get signUpTerms => + '我同意 服務條款隱私政策'; + + @override + String get termsOfServicesTitle => '服務條款'; + + @override + String get privacyPolicyTitle => '隱私政策'; + + @override + String get ackPasswordLostWarning => + '我明白,如果我遺失密碼,我可能會遺失我的資料,因為我的資料是 端到端加密的。'; + + @override + String get encryption => '加密'; + + @override + String get logInLabel => '登錄'; + + @override + String get welcomeBack => '歡迎回來!'; + + @override + String get loginTerms => + '點選登錄後,我同意 服務條款隱私政策'; + + @override + String get noInternetConnection => '無網際網路連接'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + '請檢查您的網際網路連接,然後重試。'; + + @override + String get verificationFailedPleaseTryAgain => '驗證失敗,請再試一次'; + + @override + String get recreatePasswordTitle => '重新建立密碼'; + + @override + String get recreatePasswordBody => + '目前裝置的功能不足以驗證您的密碼,但我們可以以適用於所有裝置的方式重新產生。\n\n請使用您的復原密鑰登錄並重新產生您的密碼(如果您願意,可以再次使用相同的密碼)。'; + + @override + String get useRecoveryKey => '使用復原密鑰'; + + @override + String get forgotPassword => '忘記密碼'; + + @override + String get changeEmail => '修改信箱'; + + @override + String get verifyEmail => '驗證電子郵件'; + + @override + String weHaveSendEmailTo(String email) { + return '我們已經傳送郵件到 $email'; + } + + @override + String get toResetVerifyEmail => '要重設您的密碼,請先驗證您的電子郵件。'; + + @override + String get checkInboxAndSpamFolder => '請檢查您的收件箱 (或者是在您的“垃圾郵件”列表內) 以完成驗證'; + + @override + String get tapToEnterCode => '點選以輸入程式碼'; + + @override + String get sendEmail => '傳送電子郵件'; + + @override + String get resendEmail => '重新傳送電子郵件'; + + @override + String get passKeyPendingVerification => '仍需驗證'; + + @override + String get loginSessionExpired => '工作階段已過期'; + + @override + String get loginSessionExpiredDetails => '您的工作階段已過期。請重新登錄。'; + + @override + String get passkeyAuthTitle => '通行金鑰驗證'; + + @override + String get waitingForVerification => '等待驗證...'; + + @override + String get tryAgain => '請再試一次'; + + @override + String get checkStatus => '檢查狀態'; + + @override + String get loginWithTOTP => '使用 TOTP 登錄'; + + @override + String get recoverAccount => '恢復帳戶'; + + @override + String get setPasswordTitle => '設定密碼'; + + @override + String get changePasswordTitle => '修改密碼'; + + @override + String get resetPasswordTitle => '重設密碼'; + + @override + String get encryptionKeys => '加密金鑰'; + + @override + String get enterPasswordToEncrypt => '輸入我們可以用來加密您的資料的密碼'; + + @override + String get enterNewPasswordToEncrypt => '輸入我們可以用來加密您的資料的新密碼'; + + @override + String get passwordWarning => + '我們不會儲存這個密碼,所以如果忘記, 我們無法解密您的資料'; + + @override + String get howItWorks => '工作原理'; + + @override + String get generatingEncryptionKeys => '正在產生加密金鑰...'; + + @override + String get passwordChangedSuccessfully => '密碼修改成功'; + + @override + String get signOutFromOtherDevices => '從其他裝置登出'; + + @override + String get signOutOtherBody => '如果您認為有人可能知道您的密碼,您可以強制所有其他使用您帳戶的裝置登出。'; + + @override + String get signOutOtherDevices => '登出其他裝置'; + + @override + String get doNotSignOut => '不要登出'; + + @override + String get generatingEncryptionKeysTitle => '正在產生加密金鑰...'; + + @override + String get continueLabel => '繼續'; + + @override + String get insecureDevice => '裝置不安全'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + '抱歉,我們無法在此裝置上產生安全金鑰。\n\n請使用其他裝置註冊。'; + + @override + String get recoveryKeyCopiedToClipboard => '復原密鑰已複製到剪貼簿'; + + @override + String get recoveryKey => '復原密鑰'; + + @override + String get recoveryKeyOnForgotPassword => '如果您忘記了密碼,恢復資料的唯一方法就是使用此金鑰。'; + + @override + String get recoveryKeySaveDescription => '我們不會儲存此金鑰,請將此24個單詞金鑰儲存在一個安全的地方。'; + + @override + String get doThisLater => '稍後再做'; + + @override + String get saveKey => '儲存金鑰'; + + @override + String get recoveryKeySaved => '復原密鑰已儲存在下載資料夾中!'; + + @override + String get noRecoveryKeyTitle => '沒有復原密鑰嗎?'; + + @override + String get twoFactorAuthTitle => '二步驟驗證'; + + @override + String get enterCodeHint => '從你的身份驗證器APP中\n輸入6位數字程式碼'; + + @override + String get lostDeviceTitle => '遺失了裝置嗎?'; + + @override + String get enterRecoveryKeyHint => '輸入您的復原密鑰'; + + @override + String get recover => '恢復'; + + @override + String get loggingOut => '正在登出...'; + + @override + String get immediately => '立即'; + + @override + String get appLock => 'APP鎖'; + + @override + String get autoLock => '自動鎖定'; + + @override + String get noSystemLockFound => '未找到系統鎖'; + + @override + String get deviceLockEnablePreSteps => '要啟用裝置鎖,請在系統設定中設定裝置密碼或螢幕鎖。'; + + @override + String get appLockDescription => '在裝置的預設鎖定螢幕和帶有 PIN 或密碼的自訂鎖定螢幕之間進行選擇。'; + + @override + String get deviceLock => '裝置鎖'; + + @override + String get pinLock => 'Pin 鎖定'; + + @override + String get autoLockFeatureDescription => 'APP進入後台後鎖定的時間'; + + @override + String get hideContent => '隱藏內容'; + + @override + String get hideContentDescriptionAndroid => '在APP切換器中隱藏APP內容並停用螢幕截圖'; + + @override + String get hideContentDescriptioniOS => '在APP切換器中隱藏APP內容'; + + @override + String get tooManyIncorrectAttempts => '錯誤的嘗試次數過多'; + + @override + String get tapToUnlock => '點選解鎖'; + + @override + String get areYouSureYouWantToLogout => '您確定要登出嗎?'; + + @override + String get yesLogout => '是的,登出'; + + @override + String get authToViewSecrets => '請進行身份驗證以查看您的金鑰'; + + @override + String get next => '下一步'; + + @override + String get setNewPassword => '設定新密碼'; + + @override + String get enterPin => '輸入 PIN 碼'; + + @override + String get setNewPin => '設定新 PIN 碼'; + + @override + String get confirm => '確認'; + + @override + String get reEnterPassword => '再次輸入密碼'; + + @override + String get reEnterPin => '再次輸入 PIN 碼'; + + @override + String get androidBiometricHint => '驗證身份'; + + @override + String get androidBiometricNotRecognized => '未能辨識,請重試。'; + + @override + String get androidBiometricSuccess => '成功'; + + @override + String get androidCancelButton => '取消'; + + @override + String get androidSignInTitle => '需要進行身份驗證'; + + @override + String get androidBiometricRequiredTitle => '需要進行生物辨識認證'; + + @override + String get androidDeviceCredentialsRequiredTitle => '需要裝置憑據'; + + @override + String get androidDeviceCredentialsSetupDescription => '需要裝置憑據'; + + @override + String get goToSettings => '前往設定'; + + @override + String get androidGoToSettingsDescription => + '您的裝置上未設定生物辨識身份驗證。轉到“設定 > 安全”以加入生物辨識身份驗證。'; + + @override + String get iOSLockOut => '生物辨識身份驗證已停用。請鎖定並解鎖螢幕以啟用該功能。'; + + @override + String get iOSOkButton => '好'; + + @override + String get emailAlreadyRegistered => '電子郵件地址已被註冊。'; + + @override + String get emailNotRegistered => '電子郵件地址未註冊。'; + + @override + String get thisEmailIsAlreadyInUse => '該電子郵件已被使用'; + + @override + String emailChangedTo(String newEmail) { + return '電子郵件已更改為 $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => '認證失敗,請重試'; + + @override + String get authenticationSuccessful => '認證成功!'; + + @override + String get sessionExpired => '工作階段已過期'; + + @override + String get incorrectRecoveryKey => '復原密鑰不正確'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => '您輸入的復原密鑰不正確'; + + @override + String get twofactorAuthenticationSuccessfullyReset => '二步驟驗證已成功重設'; + + @override + String get yourVerificationCodeHasExpired => '您的驗證碼已過期'; + + @override + String get incorrectCode => '驗證碼錯誤'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您輸入的驗證碼不正確'; + + @override + String get developerSettings => '開發者設定'; + + @override + String get serverEndpoint => '伺服器端點'; + + @override + String get invalidEndpoint => '端點無效'; + + @override + String get invalidEndpointMessage => '抱歉,您輸入的端點無效。請輸入有效的端點,然後重試。'; + + @override + String get endpointUpdatedMessage => '端點更新成功'; } From 93f32de8c1489b263b57e0dcae6b591e7386b649 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 22 Jul 2025 00:14:27 +0530 Subject: [PATCH 071/164] Update strings From 6af494206e5cbc44f3ac1083d1ff0c407dd78db1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 22 Jul 2025 00:15:51 +0530 Subject: [PATCH 072/164] Add developer settings page --- .../ui/lib/pages/developer_settings_page.dart | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 mobile/packages/ui/lib/pages/developer_settings_page.dart diff --git a/mobile/packages/ui/lib/pages/developer_settings_page.dart b/mobile/packages/ui/lib/pages/developer_settings_page.dart new file mode 100644 index 0000000000..0b3131631d --- /dev/null +++ b/mobile/packages/ui/lib/pages/developer_settings_page.dart @@ -0,0 +1,92 @@ +import 'package:dio/dio.dart'; +import "package:ente_configuration/base_configuration.dart"; +import "package:ente_logging/logging.dart"; +import "package:ente_strings/ente_strings.dart"; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_ui/utils/toast_util.dart'; +import 'package:flutter/material.dart'; + +class DeveloperSettingsPage extends StatefulWidget { + final BaseConfiguration config; + + const DeveloperSettingsPage(this.config, {super.key}); + + @override + State createState() => _DeveloperSettingsPageState(); +} + +class _DeveloperSettingsPageState extends State { + final _logger = Logger('DeveloperSettingsPage'); + final _urlController = TextEditingController(); + + @override + void dispose() { + _urlController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _logger.info( + "Current endpoint is: ${widget.config.getHttpEndpoint()}", + ); + return Scaffold( + appBar: AppBar( + title: Text(context.strings.developerSettings), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + TextField( + controller: _urlController, + decoration: InputDecoration( + labelText: context.strings.serverEndpoint, + hintText: widget.config.getHttpEndpoint(), + ), + autofocus: true, + ), + const SizedBox(height: 40), + GradientButton( + onTap: () async { + final String url = _urlController.text; + _logger.info("Entered endpoint: $url"); + try { + final uri = Uri.parse(url); + if ((uri.scheme == "http" || uri.scheme == "https")) { + await _ping(url); + await widget.config.setHttpEndpoint(url); + showToast(context, context.strings.endpointUpdatedMessage); + Navigator.of(context).pop(); + } else { + throw const FormatException(); + } + } catch (e) { + // ignore: unawaited_futures + showErrorDialog( + context, + context.strings.invalidEndpoint, + context.strings.invalidEndpointMessage, + ); + } + }, + text: context.strings.save, + ), + ], + ), + ), + ); + } + + Future _ping(String endpoint) async { + try { + final response = await Dio().get('$endpoint/ping'); + if (response.data['message'] != 'pong') { + throw Exception('Invalid response'); + } + } catch (e) { + throw Exception('Error occurred: $e'); + } + } +} From 3e888876d14fa94118f78c68a01dd8574f56e614 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 13:58:26 +0530 Subject: [PATCH 073/164] Update utils --- mobile/packages/utils/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 4ec3dd3c90..cdce22ee00 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -10,8 +10,8 @@ environment: dependencies: archive: ^4.0.7 email_validator: ^3.0.0 - ente_configuration: - path: ../../packages/configuration + file_saver: ^0.3.0 + flutter_email_sender: ^7.0.0 ente_logging: path: ../../packages/logging ente_strings: From d7986b5c7c86a74b00d1fb5f27f5e22af4f34e65 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 13:58:26 +0530 Subject: [PATCH 074/164] Update utils From df74c7d54d56b8e36767eadce10ec92adefdd5f9 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:38:15 +0530 Subject: [PATCH 075/164] Update strings --- .../strings/lib/l10n/arb/strings_en.arb | 239 ----- .../lib/l10n/strings_localizations.dart | 348 ------- .../lib/l10n/strings_localizations_ar.dart | 396 ++------ .../lib/l10n/strings_localizations_bg.dart | 398 ++------ .../lib/l10n/strings_localizations_cs.dart | 392 ++----- .../lib/l10n/strings_localizations_da.dart | 388 ++----- .../lib/l10n/strings_localizations_el.dart | 400 ++------ .../lib/l10n/strings_localizations_en.dart | 191 ---- .../lib/l10n/strings_localizations_es.dart | 400 ++------ .../lib/l10n/strings_localizations_fr.dart | 329 ++++++ .../lib/l10n/strings_localizations_id.dart | 397 ++------ .../lib/l10n/strings_localizations_ja.dart | 331 +++++- .../lib/l10n/strings_localizations_ko.dart | 330 ++++++ .../lib/l10n/strings_localizations_lt.dart | 395 ++------ .../lib/l10n/strings_localizations_nl.dart | 399 ++------ .../lib/l10n/strings_localizations_pl.dart | 395 ++------ .../lib/l10n/strings_localizations_pt.dart | 396 ++------ .../lib/l10n/strings_localizations_ru.dart | 403 ++------ .../lib/l10n/strings_localizations_sk.dart | 392 ++----- .../lib/l10n/strings_localizations_sr.dart | 397 ++------ .../lib/l10n/strings_localizations_sv.dart | 395 ++------ .../lib/l10n/strings_localizations_tr.dart | 399 ++------ .../lib/l10n/strings_localizations_vi.dart | 393 ++------ .../lib/l10n/strings_localizations_zh.dart | 954 +++--------------- 24 files changed, 2827 insertions(+), 6630 deletions(-) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index 6db1039109..c9eb17a1ab 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -540,244 +540,5 @@ "recover": "Recover", "@recover": { "description": "Recover button label" - }, - "loggingOut": "Logging out...", - "@loggingOut": { - "description": "Message shown while logging out" - }, - "immediately": "Immediately", - "@immediately": { - "description": "Immediately option for auto lock timing" - }, - "appLock": "App lock", - "@appLock": { - "description": "App lock setting title" - }, - "autoLock": "Auto lock", - "@autoLock": { - "description": "Auto lock setting title" - }, - "noSystemLockFound": "No system lock found", - "@noSystemLockFound": { - "description": "Error when no system lock is found" - }, - "deviceLockEnablePreSteps": "To enable device lock, please setup device passcode or screen lock in your system settings.", - "@deviceLockEnablePreSteps": { - "description": "Instructions for enabling device lock" - }, - "appLockDescription": "Choose between your device's default lock screen and a custom lock screen with a PIN or password.", - "@appLockDescription": { - "description": "Description of app lock feature" - }, - "deviceLock": "Device lock", - "@deviceLock": { - "description": "Device lock option title" - }, - "pinLock": "Pin lock", - "@pinLock": { - "description": "PIN lock option title" - }, - "autoLockFeatureDescription": "Time after which the app locks after being put in the background", - "@autoLockFeatureDescription": { - "description": "Description of auto lock feature" - }, - "hideContent": "Hide content", - "@hideContent": { - "description": "Hide content setting title" - }, - "hideContentDescriptionAndroid": "Hides app content in the app switcher and disables screenshots", - "@hideContentDescriptionAndroid": { - "description": "Description of hide content feature on Android" - }, - "hideContentDescriptioniOS": "Hides app content in the app switcher", - "@hideContentDescriptioniOS": { - "description": "Description of hide content feature on iOS" - }, - "tooManyIncorrectAttempts": "Too many incorrect attempts", - "@tooManyIncorrectAttempts": { - "description": "Message shown when too many incorrect attempts are made" - }, - "tapToUnlock": "Tap to unlock", - "@tapToUnlock": { - "description": "Message prompting user to tap to unlock" - }, - "areYouSureYouWantToLogout": "Are you sure you want to logout?", - "@areYouSureYouWantToLogout": { - "description": "Confirmation message before logout" - }, - "yesLogout": "Yes, logout", - "@yesLogout": { - "description": "Confirmation button for logout" - }, - "authToViewSecrets": "Please authenticate to view your secrets", - "@authToViewSecrets": { - "description": "Message prompting authentication to view secrets" - }, - "next": "Next", - "@next": { - "description": "Next button label" - }, - "setNewPassword": "Set new password", - "@setNewPassword": { - "description": "Set new password title" - }, - "enterPin": "Enter PIN", - "@enterPin": { - "description": "Enter PIN prompt" - }, - "setNewPin": "Set new PIN", - "@setNewPin": { - "description": "Set new PIN title" - }, - "confirm": "Confirm", - "@confirm": { - "description": "Confirm button label" - }, - "reEnterPassword": "Re-enter password", - "@reEnterPassword": { - "description": "Re-enter password prompt" - }, - "reEnterPin": "Re-enter PIN", - "@reEnterPin": { - "description": "Re-enter PIN prompt" - }, - "androidBiometricHint": "Verify identity", - "@androidBiometricHint": { - "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." - }, - "androidBiometricNotRecognized": "Not recognized. Try again.", - "@androidBiometricNotRecognized": { - "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." - }, - "androidBiometricSuccess": "Success", - "@androidBiometricSuccess": { - "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." - }, - "androidCancelButton": "Cancel", - "@androidCancelButton": { - "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." - }, - "androidSignInTitle": "Authentication required", - "@androidSignInTitle": { - "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." - }, - "androidBiometricRequiredTitle": "Biometric required", - "@androidBiometricRequiredTitle": { - "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." - }, - "androidDeviceCredentialsRequiredTitle": "Device credentials required", - "@androidDeviceCredentialsRequiredTitle": { - "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." - }, - "androidDeviceCredentialsSetupDescription": "Device credentials required", - "@androidDeviceCredentialsSetupDescription": { - "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." - }, - "goToSettings": "Go to settings", - "@goToSettings": { - "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." - }, - "androidGoToSettingsDescription": "Biometric authentication is not set up on your device. Go to 'Settings > Security' to add biometric authentication.", - "@androidGoToSettingsDescription": { - "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." - }, - "iOSLockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.", - "@iOSLockOut": { - "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." - }, - "iOSOkButton": "OK", - "@iOSOkButton": { - "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." - }, - "emailAlreadyRegistered": "Email already registered.", - "@emailAlreadyRegistered": { - "description": "Error message when email is already registered" - }, - "emailNotRegistered": "Email not registered.", - "@emailNotRegistered": { - "description": "Error message when email is not registered" - }, - "thisEmailIsAlreadyInUse": "This email is already in use", - "@thisEmailIsAlreadyInUse": { - "description": "Error message when email is already in use" - }, - "emailChangedTo": "Email changed to {newEmail}", - "@emailChangedTo": { - "description": "Message when email has been changed", - "placeholders": { - "newEmail": { - "description": "The new email address", - "type": "String", - "example": "new@example.com" - } - } - }, - "authenticationFailedPleaseTryAgain": "Authentication failed, please try again", - "@authenticationFailedPleaseTryAgain": { - "description": "Error message when authentication fails" - }, - "authenticationSuccessful": "Authentication successful!", - "@authenticationSuccessful": { - "description": "Success message when authentication is successful" - }, - "sessionExpired": "Session expired", - "@sessionExpired": { - "description": "Error message when session has expired" - }, - "incorrectRecoveryKey": "Incorrect recovery key", - "@incorrectRecoveryKey": { - "description": "Error message when recovery key is incorrect" - }, - "theRecoveryKeyYouEnteredIsIncorrect": "The recovery key you entered is incorrect", - "@theRecoveryKeyYouEnteredIsIncorrect": { - "description": "Detailed error message when recovery key is incorrect" - }, - "twofactorAuthenticationSuccessfullyReset": "Two-factor authentication successfully reset", - "@twofactorAuthenticationSuccessfullyReset": { - "description": "Message when two-factor authentication is successfully reset" - }, - "noRecoveryKey": "No recovery key", - "@noRecoveryKey": { - "description": "Error message when no recovery key is found" - }, - "yourAccountHasBeenDeleted": "Your account has been deleted", - "@yourAccountHasBeenDeleted": { - "description": "Confirmation message when account has been deleted" - }, - "verificationId": "Verification ID", - "@verificationId": { - "description": "Label for verification ID" - }, - "yourVerificationCodeHasExpired": "Your verification code has expired", - "@yourVerificationCodeHasExpired": { - "description": "Error message when verification code has expired" - }, - "incorrectCode": "Incorrect code", - "@incorrectCode": { - "description": "Error message when code is incorrect" - }, - "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, the code you've entered is incorrect", - "@sorryTheCodeYouveEnteredIsIncorrect": { - "description": "Detailed error message when code is incorrect" - }, - "developerSettings": "Developer settings", - "@developerSettings": { - "description": "Label for developer settings" - }, - "serverEndpoint": "Server endpoint", - "@serverEndpoint": { - "description": "Label for server endpoint setting" - }, - "invalidEndpoint": "Invalid endpoint", - "@invalidEndpoint": { - "description": "Error message when endpoint is invalid" - }, - "invalidEndpointMessage": "Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.", - "@invalidEndpointMessage": { - "description": "Detailed error message when endpoint is invalid" - }, - "endpointUpdatedMessage": "Endpoint updated successfully", - "@endpointUpdatedMessage": { - "description": "Success message when endpoint is updated" } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index c0ec261268..3fc9ae3d62 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -946,354 +946,6 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'Recover'** String get recover; - - /// Message shown while logging out - /// - /// In en, this message translates to: - /// **'Logging out...'** - String get loggingOut; - - /// Immediately option for auto lock timing - /// - /// In en, this message translates to: - /// **'Immediately'** - String get immediately; - - /// App lock setting title - /// - /// In en, this message translates to: - /// **'App lock'** - String get appLock; - - /// Auto lock setting title - /// - /// In en, this message translates to: - /// **'Auto lock'** - String get autoLock; - - /// Error when no system lock is found - /// - /// In en, this message translates to: - /// **'No system lock found'** - String get noSystemLockFound; - - /// Instructions for enabling device lock - /// - /// In en, this message translates to: - /// **'To enable device lock, please setup device passcode or screen lock in your system settings.'** - String get deviceLockEnablePreSteps; - - /// Description of app lock feature - /// - /// In en, this message translates to: - /// **'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'** - String get appLockDescription; - - /// Device lock option title - /// - /// In en, this message translates to: - /// **'Device lock'** - String get deviceLock; - - /// PIN lock option title - /// - /// In en, this message translates to: - /// **'Pin lock'** - String get pinLock; - - /// Description of auto lock feature - /// - /// In en, this message translates to: - /// **'Time after which the app locks after being put in the background'** - String get autoLockFeatureDescription; - - /// Hide content setting title - /// - /// In en, this message translates to: - /// **'Hide content'** - String get hideContent; - - /// Description of hide content feature on Android - /// - /// In en, this message translates to: - /// **'Hides app content in the app switcher and disables screenshots'** - String get hideContentDescriptionAndroid; - - /// Description of hide content feature on iOS - /// - /// In en, this message translates to: - /// **'Hides app content in the app switcher'** - String get hideContentDescriptioniOS; - - /// Message shown when too many incorrect attempts are made - /// - /// In en, this message translates to: - /// **'Too many incorrect attempts'** - String get tooManyIncorrectAttempts; - - /// Message prompting user to tap to unlock - /// - /// In en, this message translates to: - /// **'Tap to unlock'** - String get tapToUnlock; - - /// Confirmation message before logout - /// - /// In en, this message translates to: - /// **'Are you sure you want to logout?'** - String get areYouSureYouWantToLogout; - - /// Confirmation button for logout - /// - /// In en, this message translates to: - /// **'Yes, logout'** - String get yesLogout; - - /// Message prompting authentication to view secrets - /// - /// In en, this message translates to: - /// **'Please authenticate to view your secrets'** - String get authToViewSecrets; - - /// Next button label - /// - /// In en, this message translates to: - /// **'Next'** - String get next; - - /// Set new password title - /// - /// In en, this message translates to: - /// **'Set new password'** - String get setNewPassword; - - /// Enter PIN prompt - /// - /// In en, this message translates to: - /// **'Enter PIN'** - String get enterPin; - - /// Set new PIN title - /// - /// In en, this message translates to: - /// **'Set new PIN'** - String get setNewPin; - - /// Confirm button label - /// - /// In en, this message translates to: - /// **'Confirm'** - String get confirm; - - /// Re-enter password prompt - /// - /// In en, this message translates to: - /// **'Re-enter password'** - String get reEnterPassword; - - /// Re-enter PIN prompt - /// - /// In en, this message translates to: - /// **'Re-enter PIN'** - String get reEnterPin; - - /// Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Verify identity'** - String get androidBiometricHint; - - /// Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Not recognized. Try again.'** - String get androidBiometricNotRecognized; - - /// Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Success'** - String get androidBiometricSuccess; - - /// Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters. - /// - /// In en, this message translates to: - /// **'Cancel'** - String get androidCancelButton; - - /// Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Authentication required'** - String get androidSignInTitle; - - /// Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Biometric required'** - String get androidBiometricRequiredTitle; - - /// Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters. - /// - /// In en, this message translates to: - /// **'Device credentials required'** - String get androidDeviceCredentialsRequiredTitle; - - /// Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side. - /// - /// In en, this message translates to: - /// **'Device credentials required'** - String get androidDeviceCredentialsSetupDescription; - - /// Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters. - /// - /// In en, this message translates to: - /// **'Go to settings'** - String get goToSettings; - - /// Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side. - /// - /// In en, this message translates to: - /// **'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'** - String get androidGoToSettingsDescription; - - /// Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side. - /// - /// In en, this message translates to: - /// **'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'** - String get iOSLockOut; - - /// Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters. - /// - /// In en, this message translates to: - /// **'OK'** - String get iOSOkButton; - - /// Error message when email is already registered - /// - /// In en, this message translates to: - /// **'Email already registered.'** - String get emailAlreadyRegistered; - - /// Error message when email is not registered - /// - /// In en, this message translates to: - /// **'Email not registered.'** - String get emailNotRegistered; - - /// Error message when email is already in use - /// - /// In en, this message translates to: - /// **'This email is already in use'** - String get thisEmailIsAlreadyInUse; - - /// Message when email has been changed - /// - /// In en, this message translates to: - /// **'Email changed to {newEmail}'** - String emailChangedTo(String newEmail); - - /// Error message when authentication fails - /// - /// In en, this message translates to: - /// **'Authentication failed, please try again'** - String get authenticationFailedPleaseTryAgain; - - /// Success message when authentication is successful - /// - /// In en, this message translates to: - /// **'Authentication successful!'** - String get authenticationSuccessful; - - /// Error message when session has expired - /// - /// In en, this message translates to: - /// **'Session expired'** - String get sessionExpired; - - /// Error message when recovery key is incorrect - /// - /// In en, this message translates to: - /// **'Incorrect recovery key'** - String get incorrectRecoveryKey; - - /// Detailed error message when recovery key is incorrect - /// - /// In en, this message translates to: - /// **'The recovery key you entered is incorrect'** - String get theRecoveryKeyYouEnteredIsIncorrect; - - /// Message when two-factor authentication is successfully reset - /// - /// In en, this message translates to: - /// **'Two-factor authentication successfully reset'** - String get twofactorAuthenticationSuccessfullyReset; - - /// Error message when no recovery key is found - /// - /// In en, this message translates to: - /// **'No recovery key'** - String get noRecoveryKey; - - /// Confirmation message when account has been deleted - /// - /// In en, this message translates to: - /// **'Your account has been deleted'** - String get yourAccountHasBeenDeleted; - - /// Label for verification ID - /// - /// In en, this message translates to: - /// **'Verification ID'** - String get verificationId; - - /// Error message when verification code has expired - /// - /// In en, this message translates to: - /// **'Your verification code has expired'** - String get yourVerificationCodeHasExpired; - - /// Error message when code is incorrect - /// - /// In en, this message translates to: - /// **'Incorrect code'** - String get incorrectCode; - - /// Detailed error message when code is incorrect - /// - /// In en, this message translates to: - /// **'Sorry, the code you\'ve entered is incorrect'** - String get sorryTheCodeYouveEnteredIsIncorrect; - - /// Label for developer settings - /// - /// In en, this message translates to: - /// **'Developer settings'** - String get developerSettings; - - /// Label for server endpoint setting - /// - /// In en, this message translates to: - /// **'Server endpoint'** - String get serverEndpoint; - - /// Error message when endpoint is invalid - /// - /// In en, this message translates to: - /// **'Invalid endpoint'** - String get invalidEndpoint; - - /// Detailed error message when endpoint is invalid - /// - /// In en, this message translates to: - /// **'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'** - String get invalidEndpointMessage; - - /// Success message when endpoint is updated - /// - /// In en, this message translates to: - /// **'Endpoint updated successfully'** - String get endpointUpdatedMessage; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index cdffa58cc6..99773a8672 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -103,523 +103,333 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get saveOnlyDescription => - 'هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا)؟'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'أدخل عنوان بريدك الإلكتروني الجديد'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'البريد الإلكتروني'; + String get email => 'Email'; @override - String get verify => 'التحقق'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'عنوان البريد الإلكتروني غير صالح'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'الرجاء إدخال بريد إلكتروني صالح.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'انتظر قليلاً...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'التحقق من كلمة المرور'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'كلمة المرور غير صحيحة'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'يرجى المحاولة مرة أخرى'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'أدخل كلمة المرور'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'أدخل كلمة المرور الخاصة بك'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'الجلسات النشطة'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'عذرًا'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'حدث خطأ ما، يرجى المحاولة مرة أخرى'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'إنهاء الجلسة؟'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'إنهاء'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'هذا الجهاز'; + String get thisDevice => 'This device'; @override - String get createAccount => 'إنشاء حساب'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'ضعيف'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'متوسط'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'قوي'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'إزالة الحساب'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'سوف نأسف لرؤيتك تذهب. هل تواجه بعض المشاكل؟'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'نعم، ارسل الملاحظات'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'لا، حذف الحساب'; + String get noDeleteAccountAction => 'No, delete account'; @override - String get initiateAccountDeleteTitle => 'الرجاء المصادقة لبدء حذف الحساب'; + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'تأكيد حذف الحساب'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'هذا الحساب مرتبط بتطبيقات Ente أخرى، إذا كنت تستخدم أحدها.\n\nسنضع موعدا لحذف بياناتك المرفوعة عبر كل تطبيقات Ente، وسيتم حذف حسابك بصورة دائمة.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'حذف'; + String get delete => 'Delete'; @override - String get createNewAccount => 'إنشاء حساب جديد'; + String get createNewAccount => 'Create new account'; @override - String get password => 'كلمة المرور'; + String get password => 'Password'; @override - String get confirmPassword => 'تأكيد كلمة المرور'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'قوة كلمة المرور: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'كيف سمعت عن Ente؟ (اختياري)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'نحن لا نتتبع تثبيت التطبيق. سيكون من المفيد إذا أخبرتنا أين وجدتنا!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'أوافق على شروط الخدمة وسياسة الخصوصية'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'الشروط'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'سياسة الخصوصية'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'أنا أفهم أنه إذا فقدت كلمة المرور الخاصة بي، قد أفقد بياناتي لأن بياناتي هي مشفرة من الند للند.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'التشفير'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'تسجيل الدخول'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'مرحبًا مجددًا!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'بالنقر على تسجيل الدخول، أوافق على شروط الخدمة و سياسة الخصوصية'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'لا يوجد اتصال بالإنترنت'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'يرجى التحقق من اتصالك بالإنترنت ثم المحاولة من جديد.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'فشل في المصادقة ، يرجى المحاولة مرة أخرى في وقت لاحق'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'إعادة كتابة كلمة المرور'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).'; + '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).'; @override - String get useRecoveryKey => 'استخدم مفتاح الاسترداد'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'هل نسيت كلمة المرور'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'غير البريد الإلكتروني'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'تأكيد البريد الإلكتروني'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'لقد أرسلنا رسالة إلى $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'لإعادة تعيين كلمة المرور الخاصة بك، يرجى التحقق من بريدك الإلكتروني أولاً.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'الرجاء التحقق من صندوق الوارد (والرسائل غير المرغوب فيها) لإكمال التحقق'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'انقر لإدخال الرمز'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'إرسال بريد إلكتروني'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'إعادة إرسال البريد الإلكتروني'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'التحقق ما زال جارٍ'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'انتهت صلاحية الجلسة'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'انتهت صلاحية جلستك. فضلا أعد تسجيل الدخول.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'التحقق من مفتاح المرور'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'بانتظار التحقق...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'حاول مرة أخرى'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'تحقق من الحالة'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => ''; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'إسترجاع الحساب'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'تعيين كلمة المرور'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'تغيير كلمة المرور'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'إعادة تعيين كلمة المرور'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'مفاتيح التشفير'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'أدخل كلمة المرور التي يمكننا استخدامها لتشفير بياناتك'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'أدخل كلمة مرور جديدة يمكننا استخدامها لتشفير بياناتك'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، لا يمكننا فك تشفير بياناتك'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'كيف يعمل'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'توليد مفاتيح التشفير...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'تم تغيير كلمة المرور بنجاح'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'إذا كنت تعتقد أن شخصا ما يعرف كلمة المرور الخاصة بك، يمكنك إجبار جميع الأجهزة الأخرى الستخدمة حاليا لحسابك على تسجيل الخروج.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'لا تقم بتسجيل الخروج'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'توليد مفاتيح التشفير...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'المتابعة'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'جهاز غير آمن'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'عذرًا، لم نتمكن من إنشاء مفاتيح آمنة على هذا الجهاز.\n\nيرجى التسجيل من جهاز مختلف.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => 'تم نسخ عبارة الاسترداد للحافظة'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'مفتاح الاسترداد'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد بياناتك هي بهذا المفتاح.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'نحن لا نخزن هذا المفتاح، يرجى حفظ مفتاح الـ 24 كلمة هذا في مكان آمن.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'قم بهذا لاحقاً'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'حفظ المفتاح'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => 'حُفِظ مفتاح الاستعادة في مجلد التنزيلات!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'لا يوجد مفتاح استرجاع؟'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'المصادقة الثنائية'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override - String get enterCodeHint => 'أدخل الرمز المكون من 6 أرقام من\nتطبيق المصادقة'; + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'جهاز مفقود ؟'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'أدخل رمز الاسترداد'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'استرداد'; - - @override - String get loggingOut => 'جاري تسجيل الخروج...'; - - @override - String get immediately => 'فورًا'; - - @override - String get appLock => 'قُفْل التطبيق'; - - @override - String get autoLock => 'قفل تلقائي'; - - @override - String get noSystemLockFound => 'لا يوجد قفل نظام'; - - @override - String get deviceLockEnablePreSteps => - 'لتفعيل قُفْل الجهاز، اضبط رمز مرور أو قُفْل الشاشة من الإعدادات'; - - @override - String get appLockDescription => 'اختر نوع قُفْل الشاشة: افتراضي أو مخصص.'; - - @override - String get deviceLock => 'قفل الجهاز'; - - @override - String get pinLock => 'قفل رقم التعريف الشخصي'; - - @override - String get autoLockFeatureDescription => - 'الوقت الذي بعده ينقفل التطبيق بعدما يوضع في الخلفية'; - - @override - String get hideContent => 'أخفِ المحتوى'; - - @override - String get hideContentDescriptionAndroid => - 'يخفي محتوى التطبيق في مبدل التطبيقات ويمنع لقطات الشاشة'; - - @override - String get hideContentDescriptioniOS => - 'يخفي محتوى التطبيق في مبدل التطبيقات'; - - @override - String get tooManyIncorrectAttempts => 'محاولات خاطئة أكثر من المسموح'; - - @override - String get tapToUnlock => 'المس لإلغاء القفل'; - - @override - String get areYouSureYouWantToLogout => - 'هل أنت متأكد من أنك تريد تسجيل الخروج؟'; - - @override - String get yesLogout => 'نعم، تسجيل الخروج'; - - @override - String get authToViewSecrets => - 'الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك'; - - @override - String get next => 'التالي'; - - @override - String get setNewPassword => 'عين كلمة مرور جديدة'; - - @override - String get enterPin => 'أدخل رقم التعريف الشخصي'; - - @override - String get setNewPin => 'عين رقم تعريف شخصي جديد'; - - @override - String get confirm => 'تأكيد'; - - @override - String get reEnterPassword => 'أعد إدخال كلمة المرور'; - - @override - String get reEnterPin => 'أعد إدخال رقم التعريف الشخصي'; - - @override - String get androidBiometricHint => 'التحقق من الهوية'; - - @override - String get androidBiometricNotRecognized => - 'لم يتم التعرف عليه. حاول مرة أخرى.'; - - @override - String get androidBiometricSuccess => 'تم بنجاح'; - - @override - String get androidCancelButton => 'إلغاء'; - - @override - String get androidSignInTitle => 'المصادقة مطلوبة'; - - @override - String get androidBiometricRequiredTitle => 'البيومترية مطلوبة'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'بيانات اعتماد الجهاز مطلوبة'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'بيانات اعتماد الجهاز مطلوبة'; - - @override - String get goToSettings => 'الانتقال إلى الإعدادات'; - - @override - String get androidGoToSettingsDescription => - 'لم يتم إعداد المصادقة الحيوية على جهازك. انتقل إلى \'الإعدادات > الأمن\' لإضافة المصادقة البيومترية.'; - - @override - String get iOSLockOut => - 'المصادقة البيومترية معطلة. الرجاء قفل الشاشة وفتح القفل لتفعيلها.'; - - @override - String get iOSOkButton => 'حسناً'; - - @override - String get emailAlreadyRegistered => 'البريد الإلكتروني مُسَجَّل من قبل.'; - - @override - String get emailNotRegistered => 'البريد الإلكتروني غير مُسَجَّل.'; - - @override - String get thisEmailIsAlreadyInUse => 'هذا البريد مستخدم مسبقاً'; - - @override - String emailChangedTo(String newEmail) { - return 'تم تغيير البريد الإلكتروني إلى $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'فشلت المصادقة. الرجاء المحاولة مرة أخرى'; - - @override - String get authenticationSuccessful => 'تمت المصادقة بنجاح!'; - - @override - String get sessionExpired => 'انتهت صَلاحِيَة الجِلسة'; - - @override - String get incorrectRecoveryKey => 'مفتاح الاسترداد غير صحيح'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'مفتاح الاسترداد الذي أدخلته غير صحيح'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'تم تحديث المصادقة الثنائية بنجاح'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => 'انتهت صلاحية رمز التحقق'; - - @override - String get incorrectCode => 'رمز غير صحيح'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'عذراً، الرمز الذي أدخلته غير صحيح'; - - @override - String get developerSettings => 'اعدادات المطور'; - - @override - String get serverEndpoint => 'نقطة طرف الخادم'; - - @override - String get invalidEndpoint => 'نقطة طرف غير صالحة'; - - @override - String get invalidEndpointMessage => - 'عذرا، نقطة الطرف التي أدخلتها غير صالحة. فضلا أدخل نقطة طرف صالحة وأعد المحاولة.'; - - @override - String get endpointUpdatedMessage => 'حُدِّثَت نقطة الطرف بنجاح'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index a79fe0c723..4e50c0742a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -103,531 +103,333 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get saveOnlyDescription => - 'Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Имейл'; + String get email => 'Email'; @override - String get verify => 'Потвърждаване'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Невалиден имейл адрес'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Моля, въведете валиден имейл адрес.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Моля изчакайте...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Потвърдете паролата'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Грешна парола'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Опитайте отново'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Въведете парола'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Въведете паролата си'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Активни сесии'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Опа'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Нещо се обърка, моля опитайте отново'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Това ще Ви изкара от профила на това устройство!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Това ще Ви изкара от профила на следното устройство:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Прекратяване на сесията?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Прекратяване'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Това устройство'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Създаване на акаунт'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Слаба'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Умерена'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Силна'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Изтриване на акаунта'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Ще съжаляваме да си тръгнете. Изправени ли сте пред някакъв проблем?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Да, изпращане на обратна връзка'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Не, изтриване на акаунта'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Моля, удостоверете се, за да инициирате изтриването на акаунта'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Потвърдете изтриването на акаунта'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Този акаунт е свързан с други приложения на Ente, ако използвате такива.\n\nВашите качени данни във всички приложения на Ente ще бъдат планирани за изтриване и акаунтът Ви ще бъде изтрит за постоянно.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Изтриване'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Създаване на нов акаунт'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Парола'; + String get password => 'Password'; @override - String get confirmPassword => 'Потвърждаване на паролата'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Сила на паролата: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Как научихте за Ente? (по избор)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Ние не проследяваме инсталиранията на приложения. Ще помогне, ако ни кажете къде ни намерихте!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Съгласявам се с условията за ползване и политиката за поверителност'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Условия'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Политика за поверителност'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Разбирам, че ако загубя паролата си, може да загубя данните си, тъй като данните ми са шифровани от край до край.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Шифроване'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Вход'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Добре дошли отново!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'С натискането на вход, се съгласявам с условията за ползване и политиката за поверителност'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Няма връзка с интернет'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Моля, проверете интернет връзката си и опитайте отново.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Неуспешно проверка, моля опитайте отново'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Създайте отново парола'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, но можем да я регенерираме по начин, който работи с всички устройства.\n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).'; + '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).'; @override - String get useRecoveryKey => 'Използвайте ключ за възстановяване'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Забравена парола'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Промяна на имейл'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Потвърдете имейла'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Изпратихме имейл до $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'За да нулирате паролата си, моля, първо потвърдете своя имейл.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Моля, проверете входящата си поща (и спама), за да завършите проверката'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Докоснете, за да въведете код'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Изпратете имейл'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Повторно изпращане на имейл'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Потвърждението все още се изчаква'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Сесията изтече'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Вашата сесия изтече. Моля влезте отново.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Удостоверяване с ключ за парола'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Изчаква се потвърждение...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Опитайте отново'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Проверка на състоянието'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Влизане с еднократен код'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Възстановяване на акаунт'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Задаване на парола'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Промяна на паролата'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Нулиране на паролата'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Ключове за шифроване'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Въведете парола, която да използваме за шифроване на Вашите данни'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Въведете нова парола, която да използваме за шифроване на Вашите данни'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Ние не съхраняваме тази парола, така че ако я забравите, не можем да дешифрираме Вашите данни'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Как работи'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => - 'Генериране на ключове за шифроване...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Паролата е променена успешно'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Излизане от други устройства'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Ако смятате, че някой може да знае паролата Ви, можете да принудите всички други устройства, използващи Вашия акаунт, да излязат.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Излизане от други устройства'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Не излизайте'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Генерират се ключове за шифроване...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Продължете'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Несигурно устройство'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'За съжаление не можахме да генерираме защитени ключове на това устройство.\n\nМоля, регистрирайте се от друго устройство.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Ключът за възстановяване е копиран в буферната памет'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Ключ за възстановяване'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Ако забравите паролата си, единственият начин да възстановите данните си е с този ключ.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Ние не съхраняваме този ключ, моля, запазете този ключ от 24 думи на сигурно място.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Направете това по-късно'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Запазване на ключа'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Ключът за възстановяване е запазен в папка за Изтегляния!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Няма ключ за възстановяване?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Двуфакторно удостоверяване'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Въведете 6-цифрения код от\nВашето приложение за удостоверяване'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Загубено устройство?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Въведете Вашия ключ за възстановяване'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Възстановяване'; - - @override - String get loggingOut => 'Излизане от профила...'; - - @override - String get immediately => 'Незабавно'; - - @override - String get appLock => 'Заключване на приложението'; - - @override - String get autoLock => 'Автоматично заключване'; - - @override - String get noSystemLockFound => 'Не е намерено заключване на системата'; - - @override - String get deviceLockEnablePreSteps => - 'За да активирате заключването на устройството, моля, задайте парола за устройството или заключване на екрана в системните настройки.'; - - @override - String get appLockDescription => - 'Изберете между заключен екран по подразбиране на Вашето устройство и персонализиран заключен екран с ПИН код или парола.'; - - @override - String get deviceLock => 'Заключване на устройството'; - - @override - String get pinLock => 'Заключване с ПИН код'; - - @override - String get autoLockFeatureDescription => - 'Време, след което приложението се заключва, след като е поставено на заден план'; - - @override - String get hideContent => 'Скриване на съдържанието'; - - @override - String get hideContentDescriptionAndroid => - 'Скрива съдържанието на приложението в превключвателя на приложения и деактивира екранните снимки'; - - @override - String get hideContentDescriptioniOS => - 'Скрива съдържанието на приложението в превключвателя на приложения'; - - @override - String get tooManyIncorrectAttempts => 'Твърде много неуспешни опити'; - - @override - String get tapToUnlock => 'Докоснете, за да отключите'; - - @override - String get areYouSureYouWantToLogout => - 'Наистина ли искате да излезете от профила си?'; - - @override - String get yesLogout => 'Да, излез'; - - @override - String get authToViewSecrets => - 'Моля, удостоверете се, за да видите Вашите кодове'; - - @override - String get next => 'Следващ'; - - @override - String get setNewPassword => 'Задаване на нова парола'; - - @override - String get enterPin => 'Въведете ПИН код'; - - @override - String get setNewPin => 'Задаване на нов ПИН код'; - - @override - String get confirm => 'Потвърждаване'; - - @override - String get reEnterPassword => 'Въведете отново паролата'; - - @override - String get reEnterPin => 'Въведете отново ПИН кода'; - - @override - String get androidBiometricHint => 'Потвърждаване на самоличността'; - - @override - String get androidBiometricNotRecognized => - 'Не е разпознат. Опитайте отново.'; - - @override - String get androidBiometricSuccess => 'Успешно'; - - @override - String get androidCancelButton => 'Отказ'; - - @override - String get androidSignInTitle => 'Необходимо е удостоверяване'; - - @override - String get androidBiometricRequiredTitle => 'Изискват се биометрични данни'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Изискват се идентификационни данни за устройството'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Изискват се идентификационни данни за устройството'; - - @override - String get goToSettings => 'Отваряне на настройките'; - - @override - String get androidGoToSettingsDescription => - 'Биометричното удостоверяване не е настроено на Вашето устройство. Отидете на „Настройки > Сигурност“, за да добавите биометрично удостоверяване.'; - - @override - String get iOSLockOut => - 'Биометричното удостоверяване е деактивирано. Моля, заключете и отключете екрана си, за да го активирате.'; - - @override - String get iOSOkButton => 'ОК'; - - @override - String get emailAlreadyRegistered => 'Имейлът вече е регистриран.'; - - @override - String get emailNotRegistered => 'Имейлът не е регистриран.'; - - @override - String get thisEmailIsAlreadyInUse => 'Този имейл вече се използва'; - - @override - String emailChangedTo(String newEmail) { - return 'Имейлът е променен на $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Неуспешно удостоверяване, моля опитайте отново'; - - @override - String get authenticationSuccessful => 'Успешно удостоверяване!'; - - @override - String get sessionExpired => 'Сесията е изтекла'; - - @override - String get incorrectRecoveryKey => 'Неправилен ключ за възстановяване'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Въведеният от Вас ключ за възстановяване е неправилен'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Двуфакторното удостоверяване бе успешно нулирано'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Вашият код за потвърждение е изтекъл'; - - @override - String get incorrectCode => 'Неправилен код'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'За съжаление кодът, който сте въвели, е неправилен'; - - @override - String get developerSettings => 'Настройки за програмисти'; - - @override - String get serverEndpoint => 'Крайна точка на сървъра'; - - @override - String get invalidEndpoint => 'Невалидна крайна точка'; - - @override - String get invalidEndpointMessage => - 'За съжаление въведената от Вас крайна точка е невалидна. Моля, въведете валидна крайна точка и опитайте отново.'; - - @override - String get endpointUpdatedMessage => 'Крайната точка е актуализирана успешно'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index f72957d5d5..0b22dccd2b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -103,525 +103,333 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get saveOnlyDescription => - 'Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'E-mail'; + String get email => 'Email'; @override - String get verify => 'Ověřit'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Neplatná e-mailová adresa'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => - 'Prosím, zadejte platnou e-mailovou adresu.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Čekejte prosím...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Ověření hesla'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Nesprávné heslo'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Zkuste to prosím znovu'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Zadejte heslo'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Zadejte své heslo'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktivní relace'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Jejda'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Něco se pokazilo. Zkuste to, prosím, znovu'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Tato akce Vás odhlásí z tohoto zařízení!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Toto Vás odhlásí z následujícího zařízení:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Ukončit relaci?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Ukončit'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Toto zařízení'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Vytvořit účet'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Slabé'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Střední'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Silné'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Odstranit účet'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Mrzí nás, že odcházíte. Máte nějaké problémy s aplikací?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ano, poslat zpětnou vazbu'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Ne, odstranit účet'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Pro zahájení odstranění účtu se, prosím, ověřte'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Potvrdit odstranění účtu'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override - String get confirmAccountDeleteMessage => ' '; + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Smazat'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Vytvořit nový účet'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Heslo'; + String get password => 'Password'; @override - String get confirmPassword => 'Potvrzení hesla'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Síla hesla: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Jak jste se dozvěděli o Ente? (volitelné)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Ne sledujeme instalace aplikace. Pomůže nám, když nám sdělíte, kde jste nás našli!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Podmínky'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Podmínky ochrany osobních údajů'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Rozumím, že při zapomenutí hesla mohu ztratit svá data, protože jsou zabezpečena koncovým šifrováním.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Šifrování'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Přihlásit se'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Vítejte zpět!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Kliknutím na přihlášení souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Žádné připojení k internetu'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Zkontrolujte, prosím, své připojení k internetu a zkuste to znovu.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Ověření selhalo, přihlaste se, prosím, znovu'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Resetovat heslo'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Aktzální zařízení není dostatečně výkonné pro ověření Vašeho hesla, ale můžeme ho regenerovat způsobem, který funguje ve všech zařízením.\n\nPřihlašte se pomocí obnovovacího klíče a znovu si vygenerujte své heslo (můžete použít opět stejné, pokud chcete).'; + '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).'; @override - String get useRecoveryKey => 'Použít obnovovací klíč'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Zapomenuté heslo'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Změnit e-mail'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Ověřit e-mail'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Odeslali jsme e-mail na $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Pro obnovení hesla obnovte, prosím, nejprve svůj e-mail.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Pro dokončení ověření prosím zkontrolujte, prosím, svou doručenou poštu (a spamy)'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Klepnutím zadejte kód'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Odeslat e-mail'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Odeslat e-mail znovu'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Ověřování stále probíhá'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Relace vypršela'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Vaše relace vypršela. Přihlaste se, prosím, znovu.'; + 'Your session has expired. Please login again.'; @override String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Čekání na ověření...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Zkusit znovu'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Zkontrolovat stav'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Přihlášení s TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Obnovit účet'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Nastavit heslo'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Změnit heslo'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Obnovit heslo'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Šifrovací klíče'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Zadejte heslo, kterým můžeme zašifrovat Vaše data'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Zadejte nové heslo, kterým můžeme šifrovat Vaše data'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Vaše heslo neuchováváme. Pokud ho zapomenete, nemůžeme Vaše data dešifrovat'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Jak to funguje'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Generování šifrovacích klíčů...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Heslo úspěšně změněno'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Odhlásit z ostatních zařízení'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Pokud si myslíte, že by někdo mohl znát Vaše heslo, můžete vynutit odhlášení ostatních zařízení používajících Váš účet.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Odhlásit z ostatních zařízení'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Neodhlašovat'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Generování šifrovacích klíčů...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Pokračovat'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Nezabezpečené zařízení'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Omlouváme se, na tomto zařízení nemůžeme vygenerovat bezpečné klíče.\n\nprosím přihlaste se z jiného zařízení.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => 'Obnovovací klíč byl zkopírován'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Obnovovací klíč'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Tento klíč je jedinou cestou pro obnovení Vašich dat, pokud zapomenete heslo.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Tento 24místný klíč neuchováváme, uschovejte ho, prosím, na bezpečném místě.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Udělat později'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Uložit klíč'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Obnovovací klíč uložen do složky Stažené soubory!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Nemáte obnovovací klíč?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Dvoufaktorové ověření'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Zadejte 6místný kód ze své autentizační aplikace'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Ztratili jste zařízení?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Zadejte svůj obnovovací klíč'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Obnovit'; - - @override - String get loggingOut => 'Odhlašování...'; - - @override - String get immediately => 'Ihned'; - - @override - String get appLock => 'Zámek aplikace'; - - @override - String get autoLock => 'Automatické zamykání'; - - @override - String get noSystemLockFound => 'Zámek systému nenalezen'; - - @override - String get deviceLockEnablePreSteps => - 'Pro aktivaci zámku zařízení si nastavte přístupový kód zařízení nebo zámek obrazovky v nastavení systému.'; - - @override - String get appLockDescription => - 'Vyberte si mezi zámkem obrazovky svého zařízení a vlastním zámkem obrazovky s PIN kódem nebo heslem.'; - - @override - String get deviceLock => 'Zámek zařízení'; - - @override - String get pinLock => 'Uzamčení na PIN'; - - @override - String get autoLockFeatureDescription => - 'Interval, po kterém se aplikace běžící na pozadí uzamkne'; - - @override - String get hideContent => 'Skrýt obsah'; - - @override - String get hideContentDescriptionAndroid => 'Skryje obsah aplikace ve '; - - @override - String get hideContentDescriptioniOS => - 'Skryje obsah aplikace při přepínání úloh'; - - @override - String get tooManyIncorrectAttempts => 'Příliš mnoho neúspěšných pokusů'; - - @override - String get tapToUnlock => 'Pro odemčení klepněte'; - - @override - String get areYouSureYouWantToLogout => 'Opravdu se chcete odhlásit?'; - - @override - String get yesLogout => 'Ano, odhlásit se'; - - @override - String get authToViewSecrets => - 'Pro zobrazení svých tajných údajů se musíte ověřit'; - - @override - String get next => 'Další'; - - @override - String get setNewPassword => 'Nastavit nové heslo'; - - @override - String get enterPin => 'Zadejte PIN'; - - @override - String get setNewPin => 'Nadra'; - - @override - String get confirm => 'Potvrdit'; - - @override - String get reEnterPassword => 'Zadejte heslo znovu'; - - @override - String get reEnterPin => 'Zadejte PIN znovu'; - - @override - String get androidBiometricHint => 'Ověřte svou identitu'; - - @override - String get androidBiometricNotRecognized => 'Nerozpoznáno. Zkuste znovu.'; - - @override - String get androidBiometricSuccess => 'Úspěch'; - - @override - String get androidCancelButton => 'Zrušit'; - - @override - String get androidSignInTitle => 'Je požadováno ověření'; - - @override - String get androidBiometricRequiredTitle => - 'Je požadováno biometrické ověření'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Jsou vyžadovány přihlašovací údaje zařízení'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Jsou vyžadovány přihlašovací údaje zařízení'; - - @override - String get goToSettings => 'Jít do nastavení'; - - @override - String get androidGoToSettingsDescription => - 'Na Vašem zařízení není nastaveno biometrické ověřování. Pro aktivaci běžte do \'Nastavení > Zabezpečení\'.'; - - @override - String get iOSLockOut => - 'Biometrické ověřování není povoleno. Pro povolení zamkněte a odemkněte obrazovku.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'E-mail je již registrován.'; - - @override - String get emailNotRegistered => 'E-mail není registrován.'; - - @override - String get thisEmailIsAlreadyInUse => 'Tento e-mail je již používán'; - - @override - String emailChangedTo(String newEmail) { - return 'E-mail změněn na $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Ověření selhalo, zkuste to, prosím, znovu'; - - @override - String get authenticationSuccessful => 'Ověření bylo úspěšné!'; - - @override - String get sessionExpired => 'Relace vypršela'; - - @override - String get incorrectRecoveryKey => 'Nesprávný obnovovací klíč'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Vámi zadaný obnovovací klíč je nesprávný'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Dvoufázové ověření bylo úspěšně obnoveno'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => 'Váš ověřovací kód vypršel'; - - @override - String get incorrectCode => 'Nesprávný kód'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Omlouváme se, zadaný kód je nesprávný'; - - @override - String get developerSettings => 'Nastavení pro vývojáře'; - - @override - String get serverEndpoint => 'Koncový bod serveru'; - - @override - String get invalidEndpoint => 'Neplatný koncový bod'; - - @override - String get invalidEndpointMessage => - 'Zadaný koncový bod je neplatný. Zadejte prosím platný koncový bod a zkuste to znovu.'; - - @override - String get endpointUpdatedMessage => 'Koncový bod byl úspěšně aktualizován'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index 82f2d03070..f641229b24 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -103,7 +103,7 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get saveOnlyDescription => - 'Vil du gemme på din enhed (Downloads mappe som udgangspunkt)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,518 +112,324 @@ class StringsLocalizationsDa extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Bekræft'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Ugyldig email adresse'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Indtast en gyldig email adresse.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Vent venligst...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Bekræft adgangskode'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Forkert adgangskode'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Forsøg venligst igen'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Indtast adgangskode'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Indtast adgangskode'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktive sessioner'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Noget gik galt, forsøg venligst igen'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Dette vil logge dig ud af denne enhed!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Dette vil logge dig ud af den følgende enhed:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Afslut session?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Afslut'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Denne enhed'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Opret konto'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Svagt'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Middel'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Stærkt'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Slet konto'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Vi er kede af at se dig gå. Er du stødt på et problem?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ja, send feedback'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Nej, slet konto'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Bekræft venligst for at påbegynde sletning af konto'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Bekræft sletning af konto'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Denne konto er forbundet til andre Ente apps, hvis du benytter nogle.\n\nDine uploadede data for alle Ente apps vil blive slettet, og din konto vil blive slettet permanent.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Slet'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Opret konto'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Kodeord'; + String get password => 'Password'; @override - String get confirmPassword => 'Bekræft kodeord'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Kodeordets styrke: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Hvordan hørte du om Ente? (valgfrit)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Vi tracker ikke app installeringer. Det ville hjælpe os at vide hvordan du fandt os!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Jeg er enig i betingelser for brug og privatlivspolitik'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Betingelser'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Privatlivspolitik'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Jeg forstår at hvis jeg mister min adgangskode kan jeg miste mine data, da mine data er end-to-end krypteret.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Kryptering'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Log ind'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Velkommen tilbage!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Ved at logge ind godkender jeg Ente\'s betingelser for brug og privatlivspolitik.'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Ingen internetforbindelse'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Tjek venligst din internetforbindelse og forsøg igen.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Bekræftelse fejlede, forsøg venligst igen'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Gendan adgangskode'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Denne enhed er ikke kraftfuld nok til at bekræfte adgangskoden, men vi kan gendanne den på en måde der fungerer for alle enheder.\n\nLog venligst ind med din gendannelsesnøgle og gendan din adgangskode (du kan bruge den samme adgangskode igen hvis du ønsker).'; + '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).'; @override - String get useRecoveryKey => 'Brug gendannelsesnøgle'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Glemt adgangskode'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Skift email adresse'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Bekræft email adresse'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Vi har sendt en email til $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'For at nulstille din adgangskode, bekræft venligst din email adresse.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Tjek venligst din indboks (og spam) for at færdiggøre verificeringen'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Tryk for at indtaste kode'; + String get tapToEnterCode => 'Tap to enter code'; @override String get sendEmail => 'Send email'; @override - String get resendEmail => 'Send email igen'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Bekræftelse afventes stadig'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Session udløbet'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Din session er udløbet. Log venligst på igen.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Bekræftelse af adgangskode'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Venter på bekræftelse...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Forsøg igen'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Tjek status'; + String get checkStatus => 'Check status'; @override String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Gendan konto'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Angiv adgangskode'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Skift adgangskode'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Nulstil adgangskode'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Krypteringsnøgler'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Indtast en adgangskode vi kan bruge til at kryptere dine data'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Indtast en ny adgangskode vi kan bruge til at kryptere dine data'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Vi gemmer ikke denne adgangskode, så hvis du glemmer den kan vi ikke dekryptere dine data'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Sådan fungerer det'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Genererer krypteringsnøgler...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Adgangskoden er blevet ændret'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Log ud af andre enheder'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Hvis du mistænker at nogen kender din adgangskode kan du tvinge alle enheder der benytter din konto til at logge ud.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Log ud af andre enheder'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Log ikke ud'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Genererer krypteringsnøgler...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Fortsæt'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Usikker enhed'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Beklager, vi kunne ikke generere sikre krypteringsnøgler på denne enhed.\n\nForsøg venligst at oprette en konto fra en anden enhed.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Gendannelsesnøgle kopieret til udklipsholderen'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Gendannelsesnøgle'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Hvis du glemmer dit kodeord er gendannelsesnøglen den eneste mulighed for at få adgang til dine data.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Vi gemmer ikke denne nøgle, gem venligst denne 24-ords nøgle et sikkert sted.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Gør det senere'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Gem nøgle'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Gendannelsesnøgle gemt i din Downloads mappe!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Ingen gendannelsesnøgle?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Tofaktorgodkendelse'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Indtast den 6-cifrede kode fra din authenticator app'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Mistet enhed?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Indtast din gendannelsesnøgle'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Gendan'; - - @override - String get loggingOut => 'Logger ud...'; - - @override - String get immediately => 'Med det samme'; - - @override - String get appLock => 'Låsning af app'; - - @override - String get autoLock => 'Automatisk lås'; - - @override - String get noSystemLockFound => 'Ingen systemlås fundet'; - - @override - String get deviceLockEnablePreSteps => - 'For at aktivere enhedslås, indstil venligst kode eller skærmlås på din enhed i dine systemindstillinger.'; - - @override - String get appLockDescription => - 'Vælg mellem din enheds standard skærmlås eller skærmlås med pinkode eller adgangskode.'; - - @override - String get deviceLock => 'Enhedslås'; - - @override - String get pinLock => 'Låsning med pinkode'; - - @override - String get autoLockFeatureDescription => - 'Tid til låsning af app efter at være blevet placeret i baggrunden'; - - @override - String get hideContent => 'Skjul indhold'; - - @override - String get hideContentDescriptionAndroid => - 'Skjul app indhold i app-vælger og deaktiver screenshots'; - - @override - String get hideContentDescriptioniOS => 'Skjul app indhold i app-vælger'; - - @override - String get tooManyIncorrectAttempts => 'For mange forkerte forsøg'; - - @override - String get tapToUnlock => 'Tryk for at låse op'; - - @override - String get areYouSureYouWantToLogout => 'Er du sikker på at du vil logge ud?'; - - @override - String get yesLogout => 'Ja, log ud'; - - @override - String get authToViewSecrets => - 'Bekræft venligst din identitet for at se dine hemmeligheder'; - - @override - String get next => 'Næste'; - - @override - String get setNewPassword => 'Indstil ny adgangskode'; - - @override - String get enterPin => 'Indtast pinkode'; - - @override - String get setNewPin => 'Indstil ny pinkode'; - - @override - String get confirm => 'Bekræft'; - - @override - String get reEnterPassword => 'Indtast adgangskode igen'; - - @override - String get reEnterPin => 'Indtast pinkode igen'; - - @override - String get androidBiometricHint => 'Bekræft identitet'; - - @override - String get androidBiometricNotRecognized => 'Ikke genkendt. Forsøg igen.'; - - @override - String get androidBiometricSuccess => 'Succes'; - - @override - String get androidCancelButton => 'Afbryd'; - - @override - String get androidSignInTitle => 'Godkendelse påkrævet'; - - @override - String get androidBiometricRequiredTitle => 'Biometri påkrævet'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Enhedsoplysninger påkrævet'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Enhedsoplysninger påkrævet'; - - @override - String get goToSettings => 'Gå til indstillinger'; - - @override - String get androidGoToSettingsDescription => - 'Biometrisk godkendelse er ikke indstillet på din enhed. Gå til \"Indstillinger > Sikkerhed\" for at indstille biometrisk godkendelse.'; - - @override - String get iOSLockOut => - 'Biometrisk godkendelse er slået fra. Lås din skærm, og lås den derefter op for at aktivere det.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'Email already registered.'; - - @override - String get emailNotRegistered => 'Email not registered.'; - - @override - String get thisEmailIsAlreadyInUse => - 'Denne email adresse er allerede i brug'; - - @override - String emailChangedTo(String newEmail) { - return 'Email adresse ændret til $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Bekræftelse af identitet fejlede, forsøg venligst igen'; - - @override - String get authenticationSuccessful => 'Bekræftelse af identitet lykkedes!'; - - @override - String get sessionExpired => 'Session udløbet'; - - @override - String get incorrectRecoveryKey => 'Forkert gendannelsesnøgle'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Den indtastede gendannelsesnøgle er ikke korrekt'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Tofaktorgodkendelse nulstillet'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Din bekræftelseskode er udløbet'; - - @override - String get incorrectCode => 'Forkert kode'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Beklager, den indtastede kode er forkert'; - - @override - String get developerSettings => 'Udvikler-indstillinger'; - - @override - String get serverEndpoint => 'Server endpoint'; - - @override - String get invalidEndpoint => 'Ugyldigt endpoint'; - - @override - String get invalidEndpointMessage => - 'Beklager, det indtastede endpoint er ugyldigt. Indtast venligst et gyldigt endpoint og forsøg igen.'; - - @override - String get endpointUpdatedMessage => 'Endpoint opdateret'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index f2af902f83..3ffe0be49b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -103,7 +103,7 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή);'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,526 +112,324 @@ class StringsLocalizationsEl extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Επαλήθευση'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Μη έγκυρη διεύθυνση email'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => - 'Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Παρακαλώ περιμένετε…'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Επαλήθευση κωδικού πρόσβασης'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Λάθος κωδικός πρόσβασης'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Παρακαλώ δοκιμάστε ξανά'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Εισάγετε κωδικό πρόσβασης'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Εισάγετε τον κωδικό πρόσβασης σας'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Ενεργές συνεδρίες'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ουπς'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Κάτι πήγε στραβά, παρακαλώ προσπαθήστε ξανά'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Αυτό θα σας αποσυνδέσει από αυτή τη συσκευή!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Αυτό θα σας αποσυνδέσει από την ακόλουθη συσκευή:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Τερματισμός συνεδρίας;'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Τερματισμός'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Αυτή η συσκευή'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Δημιουργία λογαριασμού'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Αδύναμος'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Μέτριος'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Δυνατός'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Διαγραφή λογαριασμού'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Λυπόμαστε που σας βλέπουμε να φεύγετε. Αντιμετωπίζετε κάποιο πρόβλημα;'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ναι, αποστολή σχολίων'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Όχι, διαγραφή λογαριασμού'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να ξεκινήσετε τη διαγραφή λογαριασμού'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Επιβεβαίωση διαγραφής λογαριασμού'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Αυτός ο λογαριασμός είναι συνδεδεμένος με άλλες εφαρμογές Ente, εάν χρησιμοποιείτε κάποια.\n\nΤα δεδομένα σας, σε όλες τις εφαρμογές Ente, θα προγραμματιστούν για διαγραφή και ο λογαριασμός σας θα διαγραφεί οριστικά.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Διαγραφή'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Δημιουργία νέου λογαριασμού'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Κωδικόs πρόσβασης'; + String get password => 'Password'; @override - String get confirmPassword => 'Επιβεβαίωση κωδικού πρόσβασης'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Ισχύς κωδικού πρόσβασης: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Πώς ακούσατε για το Ente; (προαιρετικό)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Δεν παρακολουθούμε εγκαταστάσεις εφαρμογών. Θα βοηθούσε αν μας είπατε πού μας βρήκατε!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Όροι'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Πολιτική Απορρήτου'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Καταλαβαίνω ότι αν χάσω τον κωδικό μου μπορεί να χάσω τα δεδομένα μου αφού τα δεδομένα μου είναι από άκρο-σε-άκρο κρυπτογραφημένα.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Kρυπτογράφηση'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Σύνδεση'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Καλωσορίσατε και πάλι!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Κάνοντας κλικ στη σύνδεση, συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Χωρίς σύνδεση στο διαδίκτυο'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Παρακαλούμε ελέγξτε τη σύνδεσή σας στο διαδίκτυο και προσπαθήστε ξανά.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Η επαλήθευση απέτυχε, παρακαλώ προσπαθήστε ξανά'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Επαναδημιουργία κωδικού πρόσβασης'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Η τρέχουσα συσκευή δεν είναι αρκετά ισχυρή για να επαληθεύσει τον κωδικό πρόσβασής σας, αλλά μπορούμε να τον αναδημιουργήσουμε με έναν τρόπο που λειτουργεί με όλες τις συσκευές.\n\nΠαρακαλούμε συνδεθείτε χρησιμοποιώντας το κλειδί ανάκτησης και αναδημιουργήστε τον κωδικό πρόσβασής σας (μπορείτε να χρησιμοποιήσετε ξανά τον ίδιο αν το επιθυμείτε).'; + '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).'; @override - String get useRecoveryKey => 'Χρήση κλειδιού ανάκτησης'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Ξέχασα τον κωδικό πρόσβασης σας'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Αλλαγή email'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Επαλήθευση email'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Έχουμε στείλει ένα μήνυμα στο $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Για να επαναφέρετε τον κωδικό πρόσβασής σας, επαληθεύστε πρώτα το email σας.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Παρακαλώ ελέγξτε τα εισερχόμενά σας (και τα ανεπιθύμητα) για να ολοκληρώσετε την επαλήθευση'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Πατήστε για να εισάγετε τον κωδικό'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Αποστολή email'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Επανάληψη αποστολής email'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => - 'Η επαλήθευση εξακολουθεί να εκκρεμεί'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Η συνεδρία έληξε'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Η συνεδρία σας έληξε. Παρακαλώ συνδεθείτε ξανά.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Επιβεβαίωση κλειδιού πρόσβασης'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Αναμονή για επαλήθευση...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Προσπαθήστε ξανά'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Έλεγχος κατάστασης'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Είσοδος με TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Ανάκτηση λογαριασμού'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Ορισμός κωδικού πρόσβασης'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Αλλαγή κωδικού πρόσβασής'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Επαναφορά κωδικού πρόσβασης'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Κλειδιά κρυπτογράφησης'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Εισάγετε έναν κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για την κρυπτογράφηση των δεδομένων σας'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Εισάγετε ένα νέο κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για να κρυπτογραφήσουμε τα δεδομένα σας'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Δεν αποθηκεύουμε αυτόν τον κωδικό πρόσβασης, οπότε αν τον ξεχάσετε δεν μπορούμε να αποκρυπτογραφήσουμε τα δεδομένα σας'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Πώς λειτουργεί'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => - 'Δημιουργία κλειδιών κρυπτογράφησης...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => - 'Ο κωδικός πρόσβασης άλλαξε επιτυχώς'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Αποσύνδεση από άλλες συσκευές'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Αν νομίζετε ότι κάποιος μπορεί να γνωρίζει τον κωδικό πρόσβασής σας, μπορείτε να αναγκάσετε όλες τις άλλες συσκευές που χρησιμοποιούν το λογαριασμό σας να αποσυνδεθούν.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Αποσύνδεση άλλων συσκευών'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Μην αποσυνδεθείτε'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Δημιουργία κλειδιών κρυπτογράφησης…'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Συνέχεια'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Μη ασφαλής συσκευή'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Συγγνώμη, δεν μπορέσαμε να δημιουργήσουμε ασφαλή κλειδιά σε αυτήν τη συσκευή.\n\nπαρακαλώ εγγραφείτε από μια διαφορετική συσκευή.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Το κλειδί ανάκτησης αντιγράφηκε στο πρόχειρο'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Κλειδί ανάκτησης'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Εάν ξεχάσετε τον κωδικό πρόσβασής σας, ο μόνος τρόπος για να ανακτήσετε τα δεδομένα σας είναι με αυτό το κλειδί.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Δεν αποθηκεύουμε αυτό το κλειδί, παρακαλώ αποθηκεύστε αυτό το κλειδί 24 λέξεων σε μια ασφαλή τοποθεσία.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Κάντε το αργότερα'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Αποθήκευση κλειδιού'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Το κλειδί ανάκτησης αποθηκεύτηκε στο φάκελο Λήψεις!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Χωρίς κλειδί ανάκτησης;'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Αυθεντικοποίηση δύο παραγόντων'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Εισάγετε τον 6ψήφιο κωδικό από \nτην εφαρμογή αυθεντικοποίησης'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Χαμένη συσκευή;'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Εισάγετε το κλειδί ανάκτησης σας'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Ανάκτηση'; - - @override - String get loggingOut => 'Αποσύνδεση…'; - - @override - String get immediately => 'Άμεσα'; - - @override - String get appLock => 'Κλείδωμα εφαρμογής'; - - @override - String get autoLock => 'Αυτόματο κλείδωμα'; - - @override - String get noSystemLockFound => 'Δεν βρέθηκε κλείδωμα συστήματος'; - - @override - String get deviceLockEnablePreSteps => - 'Για να ενεργοποιήσετε το κλείδωμα της συσκευής, παρακαλώ ρυθμίστε τον κωδικό πρόσβασης της συσκευής ή το κλείδωμα οθόνης στις ρυθμίσεις του συστήματός σας.'; - - @override - String get appLockDescription => - 'Επιλέξτε ανάμεσα στην προεπιλεγμένη οθόνη κλειδώματος της συσκευής σας και σε μια προσαρμοσμένη οθόνη κλειδώματος με ένα PIN ή έναν κωδικό πρόσβασης.'; - - @override - String get deviceLock => 'Κλείδωμα συσκευής'; - - @override - String get pinLock => 'Κλείδωμα καρφιτσωμάτων'; - - @override - String get autoLockFeatureDescription => - 'Χρόνος μετά τον οποίο η εφαρμογή κλειδώνει μετά την τοποθέτηση στο παρασκήνιο'; - - @override - String get hideContent => 'Απόκρυψη περιεχομένου'; - - @override - String get hideContentDescriptionAndroid => - 'Απόκρυψη περιεχομένου εφαρμογής στην εναλλαγή εφαρμογών και απενεργοποίηση στιγμιότυπων οθόνης'; - - @override - String get hideContentDescriptioniOS => - 'Απόκρυψη περιεχομένου εφαρμογών στην εναλλαγή εφαρμογών'; - - @override - String get tooManyIncorrectAttempts => 'Πάρα πολλές εσφαλμένες προσπάθειες'; - - @override - String get tapToUnlock => 'Πατήστε για ξεκλείδωμα'; - - @override - String get areYouSureYouWantToLogout => - 'Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;'; - - @override - String get yesLogout => 'Ναι, αποσύνδεση'; - - @override - String get authToViewSecrets => - 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε τα μυστικά σας'; - - @override - String get next => 'Επόμενο'; - - @override - String get setNewPassword => 'Ορίστε νέο κωδικό πρόσβασης'; - - @override - String get enterPin => 'Εισαγωγή PIN'; - - @override - String get setNewPin => 'Ορίστε νέο PIN'; - - @override - String get confirm => 'Επιβεβαίωση'; - - @override - String get reEnterPassword => 'Πληκτρολογήστε ξανά τον κωδικό πρόσβασης'; - - @override - String get reEnterPin => 'Πληκτρολογήστε ξανά το PIN'; - - @override - String get androidBiometricHint => 'Επαλήθευση ταυτότητας'; - - @override - String get androidBiometricNotRecognized => - 'Δεν αναγνωρίζεται. Δοκιμάστε ξανά.'; - - @override - String get androidBiometricSuccess => 'Επιτυχία'; - - @override - String get androidCancelButton => 'Ακύρωση'; - - @override - String get androidSignInTitle => 'Απαιτείται έλεγχος ταυτότητας'; - - @override - String get androidBiometricRequiredTitle => 'Απαιτούνται βιομετρικά'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Απαιτούνται στοιχεία συσκευής'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Απαιτούνται στοιχεία συσκευής'; - - @override - String get goToSettings => 'Μετάβαση στις ρυθμίσεις'; - - @override - String get androidGoToSettingsDescription => - 'Η βιομετρική πιστοποίηση δεν έχει ρυθμιστεί στη συσκευή σας. Μεταβείτε στις \'Ρυθμίσεις > Ασφάλεια\' για να προσθέσετε βιομετρική ταυτοποίηση.'; - - @override - String get iOSLockOut => - 'Η βιομετρική ταυτοποίηση είναι απενεργοποιημένη. Παρακαλώ κλειδώστε και ξεκλειδώστε την οθόνη σας για να την ενεργοποιήσετε.'; - - @override - String get iOSOkButton => 'ΟΚ'; - - @override - String get emailAlreadyRegistered => 'Email already registered.'; - - @override - String get emailNotRegistered => 'Email not registered.'; - - @override - String get thisEmailIsAlreadyInUse => 'Αυτό το email είναι ήδη σε χρήση'; - - @override - String emailChangedTo(String newEmail) { - return 'Το email άλλαξε σε $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Αποτυχία ελέγχου ταυτότητας, παρακαλώ προσπαθήστε ξανά'; - - @override - String get authenticationSuccessful => 'Επιτυχής έλεγχος ταυτότητας!'; - - @override - String get sessionExpired => 'Η συνεδρία έληξε'; - - @override - String get incorrectRecoveryKey => 'Εσφαλμένο κλειδί ανάκτησης'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Το κλειδί ανάκτησης που εισάγατε είναι εσφαλμένο'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Η αυθεντικοποίηση δύο παραγόντων επαναφέρθηκε επιτυχώς'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Ο κωδικός επαλήθευσης σας έχει λήξει'; - - @override - String get incorrectCode => 'Εσφαλμένος κωδικός'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Λυπούμαστε, ο κωδικός που εισαγάγατε είναι εσφαλμένος'; - - @override - String get developerSettings => 'Ρυθμίσεις προγραμματιστή'; - - @override - String get serverEndpoint => 'Τερματικό σημείο διακομιστή'; - - @override - String get invalidEndpoint => 'Μη έγκυρο τερματικό σημείο'; - - @override - String get invalidEndpointMessage => - 'Λυπούμαστε, το τερματικό σημείο που εισάγατε δεν είναι έγκυρο. Παρακαλώ εισάγετε ένα έγκυρο τερματικό σημείο και προσπαθήστε ξανά.'; - - @override - String get endpointUpdatedMessage => - 'Το τερματκό σημείο ενημερώθηκε επιτυχώς'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 37baaf6702..018db17f10 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -432,195 +432,4 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get recover => 'Recover'; - - @override - String get loggingOut => 'Logging out...'; - - @override - String get immediately => 'Immediately'; - - @override - String get appLock => 'App lock'; - - @override - String get autoLock => 'Auto lock'; - - @override - String get noSystemLockFound => 'No system lock found'; - - @override - String get deviceLockEnablePreSteps => - 'To enable device lock, please setup device passcode or screen lock in your system settings.'; - - @override - String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; - - @override - String get deviceLock => 'Device lock'; - - @override - String get pinLock => 'Pin lock'; - - @override - String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; - - @override - String get hideContent => 'Hide content'; - - @override - String get hideContentDescriptionAndroid => - 'Hides app content in the app switcher and disables screenshots'; - - @override - String get hideContentDescriptioniOS => - 'Hides app content in the app switcher'; - - @override - String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; - - @override - String get tapToUnlock => 'Tap to unlock'; - - @override - String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; - - @override - String get yesLogout => 'Yes, logout'; - - @override - String get authToViewSecrets => 'Please authenticate to view your secrets'; - - @override - String get next => 'Next'; - - @override - String get setNewPassword => 'Set new password'; - - @override - String get enterPin => 'Enter PIN'; - - @override - String get setNewPin => 'Set new PIN'; - - @override - String get confirm => 'Confirm'; - - @override - String get reEnterPassword => 'Re-enter password'; - - @override - String get reEnterPin => 'Re-enter PIN'; - - @override - String get androidBiometricHint => 'Verify identity'; - - @override - String get androidBiometricNotRecognized => 'Not recognized. Try again.'; - - @override - String get androidBiometricSuccess => 'Success'; - - @override - String get androidCancelButton => 'Cancel'; - - @override - String get androidSignInTitle => 'Authentication required'; - - @override - String get androidBiometricRequiredTitle => 'Biometric required'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Device credentials required'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Device credentials required'; - - @override - String get goToSettings => 'Go to settings'; - - @override - String get androidGoToSettingsDescription => - 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; - - @override - String get iOSLockOut => - 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'Email already registered.'; - - @override - String get emailNotRegistered => 'Email not registered.'; - - @override - String get thisEmailIsAlreadyInUse => 'This email is already in use'; - - @override - String emailChangedTo(String newEmail) { - return 'Email changed to $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Authentication failed, please try again'; - - @override - String get authenticationSuccessful => 'Authentication successful!'; - - @override - String get sessionExpired => 'Session expired'; - - @override - String get incorrectRecoveryKey => 'Incorrect recovery key'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'The recovery key you entered is incorrect'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Two-factor authentication successfully reset'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Your verification code has expired'; - - @override - String get incorrectCode => 'Incorrect code'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, the code you\'ve entered is incorrect'; - - @override - String get developerSettings => 'Developer settings'; - - @override - String get serverEndpoint => 'Server endpoint'; - - @override - String get invalidEndpoint => 'Invalid endpoint'; - - @override - String get invalidEndpointMessage => - 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; - - @override - String get endpointUpdatedMessage => 'Endpoint updated successfully'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index d93fcd07e8..2c9db57cf2 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -103,533 +103,333 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get saveOnlyDescription => - '¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Correo electrónico'; + String get email => 'Email'; @override - String get verify => 'Verificar'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Dirección de correo electrónico no válida'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => - 'Por favor, introduce una dirección de correo electrónico válida.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Por favor, espere...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Verificar contraseña'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Contraseña incorrecta'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Por favor, inténtalo de nuevo'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Introduzca la contraseña'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Introduce tu contraseña'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Sesiones activas'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Algo ha ido mal, por favor, inténtelo de nuevo'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - '¡Esto cerrará la sesión de este dispositivo!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Esto cerrará la sesión del siguiente dispositivo:'; + 'This will log you out of the following device:'; @override - String get terminateSession => '¿Terminar sesión?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Terminar'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Este dispositivo'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Crear cuenta'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Poco segura'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Moderada'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Segura'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Eliminar cuenta'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Lamentamos que te vayas. ¿Estás teniendo algún problema?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Sí, enviar comentarios'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'No, eliminar cuenta'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Por favor, autentícate para iniciar la eliminación de la cuenta'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Confirmar eliminación de la cuenta'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Esta cuenta está vinculada a otras aplicaciones de Ente, si utilizas alguna. \n\nSe programará la eliminación de los datos cargados en todas las aplicaciones de Ente, y tu cuenta se eliminará permanentemente.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Borrar'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Crear cuenta nueva'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Contraseña'; + String get password => 'Password'; @override - String get confirmPassword => 'Confirmar contraseña'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Fortaleza de la contraseña: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => '¿Cómo conoció Ente? (opcional)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'No rastreamos la instalación de las aplicaciones. ¡Nos ayudaría si nos dijera dónde nos encontró!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Estoy de acuerdo con los términos del servicio y la política de privacidad'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Términos'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Política de Privacidad'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Entiendo que si pierdo mi contraseña podría perder mis datos, ya que mis datos están cifrados de extremo a extremo.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Cifrado'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Iniciar sesión'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => '¡Te damos la bienvenida otra vez!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Al hacer clic en iniciar sesión, acepto los términos de servicio y la política de privacidad'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'No hay conexión a Internet'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Compruebe su conexión a Internet e inténtelo de nuevo.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Verificación fallida, por favor inténtalo de nuevo'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Recrear contraseña'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'El dispositivo actual no es lo suficientemente potente para verificar su contraseña, pero podemos regenerarla de manera que funcione con todos los dispositivos.\n\nPor favor inicie sesión usando su clave de recuperación y regenere su contraseña (puede volver a utilizar la misma si lo desea).'; + '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).'; @override - String get useRecoveryKey => 'Usar clave de recuperación'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Olvidé mi contraseña'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Cambiar correo electrónico'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Verificar correo electrónico'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Hemos enviado un correo electrónico a $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Para restablecer tu contraseña, por favor verifica tu correo electrónico primero.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Por favor revisa tu bandeja de entrada (y spam) para completar la verificación'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Toca para introducir el código'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Enviar correo electrónico'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Reenviar correo electrónico'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => - 'La verificación todavía está pendiente'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'La sesión ha expirado'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Tu sesión ha expirado. Por favor, vuelve a iniciar sesión.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Verificación de clave de acceso'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Esperando verificación...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Inténtelo de nuevo'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Comprobar estado'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Inicio de sesión con TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Recuperar cuenta'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Establecer contraseña'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Cambiar contraseña'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Restablecer contraseña'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Claves de cifrado'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Introduzca una contraseña que podamos usar para cifrar sus datos'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Introduzca una contraseña nueva que podamos usar para cifrar sus datos'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'No almacenamos esta contraseña, así que si la olvidas, no podremos descifrar tus datos'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Cómo funciona'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Generando claves de cifrado...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Contraseña cambiada correctamente'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Cerrar sesión en otros dispositivos'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Si crees que alguien puede conocer tu contraseña, puedes forzar a todos los demás dispositivos que usen tu cuenta a cerrar la sesión.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Cerrar la sesión en otros dispositivos'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'No cerrar la sesión'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Generando claves de cifrado...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Continuar'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Dispositivo inseguro'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Lo sentimos, no hemos podido generar claves seguras en este dispositivo.\n\nRegístrate desde un dispositivo diferente.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Clave de recuperación copiada al portapapeles'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Clave de recuperación'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Si olvidas tu contraseña, la única forma de recuperar tus datos es con esta clave.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Nosotros no almacenamos esta clave, por favor guarda esta clave de 24 palabras en un lugar seguro.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Hacer esto más tarde'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Guardar clave'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - '¡Clave de recuperación guardada en la carpeta Descargas!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => '¿No tienes la clave de recuperación?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Autenticación de dos factores'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Ingrese el código de seis dígitos de su aplicación de autenticación'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => '¿Dispositivo perdido?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Introduce tu clave de recuperación'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Recuperar'; - - @override - String get loggingOut => 'Cerrando sesión...'; - - @override - String get immediately => 'Inmediatamente'; - - @override - String get appLock => 'Bloqueo de aplicación'; - - @override - String get autoLock => 'Bloqueo automático'; - - @override - String get noSystemLockFound => 'Bloqueo del sistema no encontrado'; - - @override - String get deviceLockEnablePreSteps => - 'Para activar el bloqueo de la aplicación, por favor configura el código de acceso del dispositivo o el bloqueo de pantalla en los ajustes de tu sistema.'; - - @override - String get appLockDescription => - 'Elija entre la pantalla de bloqueo por defecto de su dispositivo y una pantalla de bloqueo personalizada con un PIN o contraseña.'; - - @override - String get deviceLock => 'Bloqueo del dispositivo'; - - @override - String get pinLock => 'Bloqueo con PIN'; - - @override - String get autoLockFeatureDescription => - 'Tiempo tras el cual la aplicación se bloquea después de ser colocada en segundo plano'; - - @override - String get hideContent => 'Ocultar contenido'; - - @override - String get hideContentDescriptionAndroid => - 'Oculta el contenido de la aplicación en el selector de aplicaciones y desactiva las capturas de pantalla'; - - @override - String get hideContentDescriptioniOS => - 'Ocultar el contenido de la aplicación en el selector de aplicaciones'; - - @override - String get tooManyIncorrectAttempts => 'Demasiados intentos incorrectos'; - - @override - String get tapToUnlock => 'Toca para desbloquear'; - - @override - String get areYouSureYouWantToLogout => - '¿Seguro que quieres cerrar la sesión?'; - - @override - String get yesLogout => 'Sí, cerrar la sesión'; - - @override - String get authToViewSecrets => - 'Por favor, autentícate para ver tus secretos'; - - @override - String get next => 'Siguiente'; - - @override - String get setNewPassword => 'Establece una nueva contraseña'; - - @override - String get enterPin => 'Ingresa el PIN'; - - @override - String get setNewPin => 'Establecer nuevo PIN'; - - @override - String get confirm => 'Confirmar'; - - @override - String get reEnterPassword => 'Reescribe tu contraseña'; - - @override - String get reEnterPin => 'Reescribe tu PIN'; - - @override - String get androidBiometricHint => 'Verificar identidad'; - - @override - String get androidBiometricNotRecognized => - 'No reconocido. Inténtalo de nuevo.'; - - @override - String get androidBiometricSuccess => 'Autenticación exitosa'; - - @override - String get androidCancelButton => 'Cancelar'; - - @override - String get androidSignInTitle => 'Se necesita autenticación biométrica'; - - @override - String get androidBiometricRequiredTitle => - 'Se necesita autenticación biométrica'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Se necesitan credenciales de dispositivo'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Se necesitan credenciales de dispositivo'; - - @override - String get goToSettings => 'Ir a Ajustes'; - - @override - String get androidGoToSettingsDescription => - 'La autenticación biométrica no está configurada en tu dispositivo. Ve a \'Ajustes > Seguridad\' para configurar la autenticación biométrica.'; - - @override - String get iOSLockOut => - 'La autenticación biométrica está deshabilitada. Por favor bloquea y desbloquea la pantalla para habilitarla.'; - - @override - String get iOSOkButton => 'Aceptar'; - - @override - String get emailAlreadyRegistered => 'Correo electrónico ya registrado.'; - - @override - String get emailNotRegistered => 'Correo electrónico no registrado.'; - - @override - String get thisEmailIsAlreadyInUse => - 'Este correo electrónico ya está en uso'; - - @override - String emailChangedTo(String newEmail) { - return 'Correo electrónico cambiado a $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Error de autenticación, por favor inténtalo de nuevo'; - - @override - String get authenticationSuccessful => '¡Autenticación exitosa!'; - - @override - String get sessionExpired => 'La sesión ha expirado'; - - @override - String get incorrectRecoveryKey => 'Clave de recuperación incorrecta'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'La clave de recuperación introducida es incorrecta'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Autenticación de doble factor restablecida con éxito'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Tu código de verificación ha expirado'; - - @override - String get incorrectCode => 'Código incorrecto'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Lo sentimos, el código que has introducido es incorrecto'; - - @override - String get developerSettings => 'Ajustes de desarrollador'; - - @override - String get serverEndpoint => 'Endpoint del servidor'; - - @override - String get invalidEndpoint => 'Endpoint no válido'; - - @override - String get invalidEndpointMessage => - 'Lo sentimos, el endpoint introducido no es válido. Por favor, introduce un endpoint válido y vuelve a intentarlo.'; - - @override - String get endpointUpdatedMessage => 'Endpoint actualizado con éxito'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 3375cff00a..0f521ac6d1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -103,6 +103,335 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; 'Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ?'; @override diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index e3839340a6..baa8ee45e6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -103,528 +103,333 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get saveOnlyDescription => - 'Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads)'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Masukkan alamat email baru anda'; + String get enterNewEmailHint => 'Enter your new email address'; @override String get email => 'Email'; @override - String get verify => 'Verifikasi'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Alamat email tidak valid'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Harap masukkan alamat email yang valid.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Mohon tunggu...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Verifikasi kata sandi'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Kata sandi salah'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Harap coba lagi'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Masukkan kata sandi'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Masukkan kata sandi Anda'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Sesi aktif'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Ada yang salah. Mohon coba kembali'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Langkah ini akan mengeluarkan Anda dari gawai ini!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Langkah ini akan mengeluarkan Anda dari gawai berikut:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Akhiri sesi?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Akhiri'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Gawai ini'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Buat akun'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Lemah'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Sedang'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Kuat'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Hapus akun'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Kami akan merasa kehilangan Anda. Apakah Anda menghadapi masalah?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ya, kirim umpan balik'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Tidak, hapus akun'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Harap autentikasi untuk memulai penghapusan akun'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Konfirmasikan penghapusan akun'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Akun ini terhubung dengan aplikasi Ente yang lain (jika Anda pakai).\n\nData yang Anda unggah di seluruh aplikasi Ente akan dijadwalkan untuk dihapus. Akun Anda juga akan dihapus secara permanen.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Hapus'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Buat akun baru'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Kata Sandi'; + String get password => 'Password'; @override - String get confirmPassword => 'Konfirmasi kata sandi'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Tingkat kekuatan kata sandi: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Dari mana Anda menemukan Ente? (opsional)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Kami tidak melacak penginstalan aplikasi kami. Akan sangat membantu kami bila Anda memberitahu kami dari mana Anda mengetahui Ente!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Ketentuan'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Kebijakan Privasi'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Saya mengerti bahwa jika saya lupa kata sandi saya, data saya dapat hilang karena data saya terenkripsi secara end-to-end.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Enkripsi'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Masuk akun'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Selamat datang kembali!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Dengan menekan masuk akun, saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Tiada koneksi internet'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Mohon periksa koneksi internet Anda dan coba kembali.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Gagal memverifikasi. Mohon coba lagi'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Membuat kembali kata sandi'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Gawai Anda saat ini tidak dapat memverifikasi kata sandi Anda. Namun, kami dapat membuat ulang dengan cara yang dapat digunakan pada semua gawai.\n\nMohon masuk log dengan kunci pemulihan dan buat ulang kata sandi Anda (kata sandi yang sama diperbolehkan).'; + '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).'; @override - String get useRecoveryKey => 'Gunakan kunci pemulihan'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Lupa kata sandi'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Ubah alamat email'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Verifikasi email'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Kami telah mengirimkan sebuah posel ke $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Untuk mengatur ulang kata sandi, mohon verifikasi surel Anda terlebih dahulu.'; + 'To reset your password, please verify your email first.'; @override - String get checkInboxAndSpamFolder => 'Mohon cek'; + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Ketuk untuk memasukkan kode'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Kirim surel'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Kirim ulang surel'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Verifikasi tertunda'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Sesi sudah berakhir'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Sesi Anda sudah berakhir. Mohon masuk log kembali.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Verifikasi passkey'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Menantikan verifikasi...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Coba lagi'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Periksa status'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Masuk menggunakan TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Pulihkan akun'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Atur kata sandi'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Ubah kata sandi'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Atur ulang kata sandi'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Kunci enkripsi'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Masukkan kata sandi yang dapat kami gunakan untuk mengenkripsi data Anda'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Masukkan kata sandi baru yang dapat kami gunakan untuk mengenkripsi data Anda'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Kami tidak menyimpan kata sandi Anda. Jika Anda lupa, kami tidak dapat mendekripsi data Anda'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Cara kerjanya'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Sedang membuat kunci enkripsi...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Kata sandi sukses diubah'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Keluar dari gawai yang lain'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Jika Anda pikir seseorang mungkin mengetahui kata sandi Anda, Anda dapat mengeluarkan akun Anda pada semua gawai'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Keluar akun pada gawai yang lain'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Jangan keluar'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Sedang membuat kunci enkripsi...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Lanjutkan'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Perangkat tidak aman'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Maaf, kami tidak dapat membuat kunci yang aman pada perangkat ini.\n\nHarap mendaftar dengan perangkat lain.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Kunci pemulihan disalin ke papan klip'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Kunci pemulihan'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Jika Anda lupa kata sandi, satu-satunya cara memulihkan data Anda adalah dengan kunci ini.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Kami tidak menyimpan kunci ini, jadi harap simpan kunci yang berisi 24 kata ini dengan aman.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Lakukan lain kali'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Simpan kunci'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Kunci pemulihan sudah tersimpan di folder \'Downloads\'!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Tidak punya kunci pemulihan?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Autentikasi dua langkah'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Masukkan kode 6 digit dari aplikasi autentikator Anda'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Perangkat hilang?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Masukkan kunci pemulihan Anda'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Pulihkan'; - - @override - String get loggingOut => 'Mengeluarkan akun...'; - - @override - String get immediately => 'Segera'; - - @override - String get appLock => 'Kunci aplikasi'; - - @override - String get autoLock => 'Kunci otomatis'; - - @override - String get noSystemLockFound => 'Tidak ditemukan kunci sistem'; - - @override - String get deviceLockEnablePreSteps => - 'Pasang kunci sandi atau kunci layar pada pengaturan sistem untuk menyalakan Pengunci Gawai.'; - - @override - String get appLockDescription => - 'Pilih layar kunci bawaan gawai Anda ATAU layar kunci kustom dengan PIN atau kata sandi.'; - - @override - String get deviceLock => 'Kunci perangkat'; - - @override - String get pinLock => 'PIN'; - - @override - String get autoLockFeatureDescription => - 'Durasi waktu aplikasi akan terkunci setelah aplikasi ditutup'; - - @override - String get hideContent => 'Sembunyikan isi'; - - @override - String get hideContentDescriptionAndroid => - 'Menyembunyikan konten aplikasi di pemilih aplikasi dan menonaktifkan tangkapan layar'; - - @override - String get hideContentDescriptioniOS => - 'Menyembunyikan konten aplikasi di pemilih aplikasi'; - - @override - String get tooManyIncorrectAttempts => 'Terlalu banyak percobaan yang salah'; - - @override - String get tapToUnlock => 'Ketuk untuk membuka'; - - @override - String get areYouSureYouWantToLogout => - 'Anda yakin ingin keluar dari akun ini?'; - - @override - String get yesLogout => 'Ya, keluar akun'; - - @override - String get authToViewSecrets => - 'Harap lakukan autentikasi untuk melihat rahasia Anda'; - - @override - String get next => 'Selanjutnya'; - - @override - String get setNewPassword => 'Pasang kata sandi baru'; - - @override - String get enterPin => 'Masukkan PIN'; - - @override - String get setNewPin => 'Pasang PIN yang baru'; - - @override - String get confirm => 'Konfirmasikan'; - - @override - String get reEnterPassword => 'Masukkan kembali kata sandi'; - - @override - String get reEnterPin => 'Masukkan kembali PIN'; - - @override - String get androidBiometricHint => 'Verifikasikan identitas Anda'; - - @override - String get androidBiometricNotRecognized => 'Tidak dikenal. Coba lagi.'; - - @override - String get androidBiometricSuccess => 'Sukses'; - - @override - String get androidCancelButton => 'Batalkan'; - - @override - String get androidSignInTitle => 'Autentikasi diperlukan'; - - @override - String get androidBiometricRequiredTitle => 'Biometrik diperlukan'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Kredensial perangkat diperlukan'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Kredensial perangkat diperlukan'; - - @override - String get goToSettings => 'Pergi ke pengaturan'; - - @override - String get androidGoToSettingsDescription => - 'Tidak ada autentikasi biometrik pada gawai Anda. Buka \'Pengaturan > Keamanan\' untuk menambahkan autentikasi biometrik pada gawai Anda.'; - - @override - String get iOSLockOut => - 'Autentikasi biometrik dimatikan. Kunci dan buka layar Anda untuk menyalakan autentikasi biometrik.'; - - @override - String get iOSOkButton => 'Oke'; - - @override - String get emailAlreadyRegistered => 'Email sudah terdaftar.'; - - @override - String get emailNotRegistered => 'Email belum terdaftar.'; - - @override - String get thisEmailIsAlreadyInUse => 'Surel ini sudah dipakai!'; - - @override - String emailChangedTo(String newEmail) { - return 'Surel sudah diganti menjadi $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Gagal mengautentikasi. Mohon coba lagi'; - - @override - String get authenticationSuccessful => 'Sukses mengautentikasi!'; - - @override - String get sessionExpired => 'Sesi berakhir'; - - @override - String get incorrectRecoveryKey => 'Kunci pemulihan takbenar'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Kunci pemulihan yang Anda masukkan takbenar'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Autentikasi dwifaktor sukses diatur ulang'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Kode verifikasi Anda telah kedaluwarsa'; - - @override - String get incorrectCode => 'Kode takbenar'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Maaf, kode yang Anda masukkan takbenar'; - - @override - String get developerSettings => 'Pengaturan Pengembang'; - - @override - String get serverEndpoint => 'Peladen endpoint'; - - @override - String get invalidEndpoint => 'Endpoint takvalid'; - - @override - String get invalidEndpointMessage => - 'Maaf, endpoint yang Anda masukkan takvalid. Mohon masukkan endpoint yang valid, lalu coba kembali.'; - - @override - String get endpointUpdatedMessage => 'Endpoint berhasil diubah'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 54766bcdb1..8947a8aaef 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -599,5 +599,334 @@ class StringsLocalizationsJa extends StringsLocalizations { '入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。'; @override - String get endpointUpdatedMessage => 'エンドポイントの更新に成功しました'; + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index bdb0abf69e..5286fa2fe0 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -601,4 +601,334 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get endpointUpdatedMessage => '엔드포인트가 성공적으로 업데이트됨'; + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; + + @override + String get enterNewEmailHint => 'Enter your new email address'; + + @override + String get email => 'Email'; + + @override + String get verify => 'Verify'; + + @override + String get invalidEmailTitle => 'Invalid email address'; + + @override + String get invalidEmailMessage => 'Please enter a valid email address.'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get verifyPassword => 'Verify password'; + + @override + String get incorrectPasswordTitle => 'Incorrect password'; + + @override + String get pleaseTryAgain => 'Please try again'; + + @override + String get enterPassword => 'Enter password'; + + @override + String get enterYourPasswordHint => 'Enter your password'; + + @override + String get activeSessions => 'Active sessions'; + + @override + String get oops => 'Oops'; + + @override + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; + + @override + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; + + @override + String get terminateSession => 'Terminate session?'; + + @override + String get terminate => 'Terminate'; + + @override + String get thisDevice => 'This device'; + + @override + String get createAccount => 'Create account'; + + @override + String get weakStrength => 'Weak'; + + @override + String get moderateStrength => 'Moderate'; + + @override + String get strongStrength => 'Strong'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; + + @override + String get yesSendFeedbackAction => 'Yes, send feedback'; + + @override + String get noDeleteAccountAction => 'No, delete account'; + + @override + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; + + @override + String get confirmAccountDeleteTitle => 'Confirm account deletion'; + + @override + String get confirmAccountDeleteMessage => + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + + @override + String get delete => 'Delete'; + + @override + String get createNewAccount => 'Create new account'; + + @override + String get password => 'Password'; + + @override + String get confirmPassword => 'Confirm password'; + + @override + String passwordStrength(String passwordStrengthValue) { + return 'Password strength: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + + @override + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; + + @override + String get signUpTerms => + 'I agree to the terms of service and privacy policy'; + + @override + String get termsOfServicesTitle => 'Terms'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get ackPasswordLostWarning => + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + + @override + String get encryption => 'Encryption'; + + @override + String get logInLabel => 'Log in'; + + @override + String get welcomeBack => 'Welcome back!'; + + @override + String get loginTerms => + 'By clicking log in, I agree to the terms of service and privacy policy'; + + @override + String get noInternetConnection => 'No internet connection'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; + + @override + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; + + @override + String get recreatePasswordTitle => 'Recreate password'; + + @override + String get recreatePasswordBody => + '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).'; + + @override + String get useRecoveryKey => 'Use recovery key'; + + @override + String get forgotPassword => 'Forgot password'; + + @override + String get changeEmail => 'Change email'; + + @override + String get verifyEmail => 'Verify email'; + + @override + String weHaveSendEmailTo(String email) { + return 'We have sent a mail to $email'; + } + + @override + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; + + @override + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; + + @override + String get tapToEnterCode => 'Tap to enter code'; + + @override + String get sendEmail => 'Send email'; + + @override + String get resendEmail => 'Resend email'; + + @override + String get passKeyPendingVerification => 'Verification is still pending'; + + @override + String get loginSessionExpired => 'Session expired'; + + @override + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; + + @override + String get passkeyAuthTitle => 'Passkey verification'; + + @override + String get waitingForVerification => 'Waiting for verification...'; + + @override + String get tryAgain => 'Try again'; + + @override + String get checkStatus => 'Check status'; + + @override + String get loginWithTOTP => 'Login with TOTP'; + + @override + String get recoverAccount => 'Recover account'; + + @override + String get setPasswordTitle => 'Set password'; + + @override + String get changePasswordTitle => 'Change password'; + + @override + String get resetPasswordTitle => 'Reset password'; + + @override + String get encryptionKeys => 'Encryption keys'; + + @override + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; + + @override + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; + + @override + String get passwordWarning => + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + + @override + String get howItWorks => 'How it works'; + + @override + String get generatingEncryptionKeys => 'Generating encryption keys...'; + + @override + String get passwordChangedSuccessfully => 'Password changed successfully'; + + @override + String get signOutFromOtherDevices => 'Sign out from other devices'; + + @override + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + + @override + String get signOutOtherDevices => 'Sign out other devices'; + + @override + String get doNotSignOut => 'Do not sign out'; + + @override + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + + @override + String get continueLabel => 'Continue'; + + @override + String get insecureDevice => 'Insecure device'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + + @override + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + + @override + String get recoveryKey => 'Recovery key'; + + @override + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; + + @override + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; + + @override + String get doThisLater => 'Do this later'; + + @override + String get saveKey => 'Save key'; + + @override + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + + @override + String get noRecoveryKeyTitle => 'No recovery key?'; + + @override + String get twoFactorAuthTitle => 'Two-factor authentication'; + + @override + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; + + @override + String get lostDeviceTitle => 'Lost device?'; + + @override + String get enterRecoveryKeyHint => 'Enter your recovery key'; + + @override + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 67514db54f..2d4a43e317 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -103,528 +103,333 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get saveOnlyDescription => - 'Ar norite tai išsaugoti savo saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'El. paštas'; + String get email => 'Email'; @override - String get verify => 'Patvirtinti'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Netinkamas el. pašto adresas'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Įveskite tinkamą el. pašto adresą.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Palaukite...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Patvirtinkite slaptažodį'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Neteisingas slaptažodis.'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Bandykite dar kartą.'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Įveskite slaptažodį'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Įveskite savo slaptažodį'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktyvūs seansai'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Kažkas nutiko ne taip. Bandykite dar kartą.'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Tai jus atjungs nuo šio įrenginio.'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Tai jus atjungs nuo toliau nurodyto įrenginio:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Baigti seansą?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Baigti'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Šis įrenginys'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Kurti paskyrą'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Silpna'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Vidutinė'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Stipri'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Ištrinti paskyrą'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Apgailestausime, kad išeinate. Ar susiduriate su kažkokiomis problemomis?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Taip, siųsti atsiliepimą'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Ne, ištrinti paskyrą'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Nustatykite tapatybę, kad pradėtumėte paskyros ištrynimą'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Patvirtinkite paskyros ištrynimą'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Ši paskyra susieta su kitomis „Ente“ programomis, jei jas naudojate.\n\nJūsų įkelti duomenys per visas „Ente“ programas bus planuojama ištrinti, o jūsų paskyra bus ištrinta negrįžtamai.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Ištrinti'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Kurti naują paskyrą'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Slaptažodis'; + String get password => 'Password'; @override - String get confirmPassword => 'Patvirtinkite slaptažodį'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Slaptažodžio stiprumas: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Kaip išgirdote apie „Ente“? (nebūtina)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote.'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Sutinku su paslaugų sąlygomis ir privatumo politika'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Sąlygos'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Privatumo politika'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Suprantu, kad jei prarasiu slaptažodį, galiu prarasti savo duomenis, kadangi duomenys yra visapusiškai užšifruota.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Šifravimas'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Prisijungti'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Sveiki sugrįžę!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Spustelėjus Prisijungti sutinku su paslaugų sąlygomis ir privatumo politika'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Nėra interneto ryšio'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Patikrinkite savo interneto ryšį ir bandykite dar kartą.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Patvirtinimas nepavyko. Bandykite dar kartą.'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Iš naujo sukurti slaptažodį'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Dabartinis įrenginys nėra pakankamai galingas, kad patvirtintų jūsų slaptažodį, bet mes galime iš naujo sugeneruoti taip, kad jis veiktų su visais įrenginiais.\n\nPrisijunkite naudodami atkūrimo raktą ir sugeneruokite iš naujo slaptažodį (jei norite, galite vėl naudoti tą patį).'; + '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).'; @override - String get useRecoveryKey => 'Naudoti atkūrimo raktą'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Pamiršau slaptažodį'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Keisti el. paštą'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Patvirtinti el. paštą'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Išsiuntėme laišką adresu $email.'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Kad iš naujo nustatytumėte slaptažodį, pirmiausia patvirtinkite savo el. paštą.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Patikrinkite savo gautieją (ir šlamštą), kad užbaigtumėte patvirtinimą.'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Palieskite, kad įvestumėte kodą'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Siųsti el. laišką'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Iš naujo siųsti el. laišką'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Vis dar laukiama patvirtinimo'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Seansas baigėsi'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Jūsų seansas baigėsi. Prisijunkite iš naujo.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Slaptarakčio patvirtinimas'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Laukiama patvirtinimo...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Bandyti dar kartą'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Tikrinti būseną'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Prisijungti su TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Atkurti paskyrą'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Nustatyti slaptažodį'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Keisti slaptažodį'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Nustatyti slaptažodį iš naujo'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Šifravimo raktai'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Įveskite slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Įveskite naują slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Šio slaptažodžio nesaugome, todėl jei jį pamiršite, negalėsime iššifruoti jūsų duomenų'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Kaip tai veikia'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Generuojami šifravimo raktai...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Slaptažodis sėkmingai pakeistas'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Atsijungti iš kitų įrenginių'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Jei manote, kad kas nors gali žinoti jūsų slaptažodį, galite priverstinai atsijungti iš visų kitų įrenginių, naudojančių jūsų paskyrą.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Atsijungti kitus įrenginius'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Neatsijungti'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Generuojami šifravimo raktai...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Tęsti'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Nesaugus įrenginys'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Atsiprašome, šiame įrenginyje nepavyko sugeneruoti saugių raktų.\n\nRegistruokitės iš kito įrenginio.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Nukopijuotas atkūrimo raktas į iškarpinę'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Atkūrimo raktas'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Jei pamiršote slaptažodį, vienintelis būdas atkurti duomenis – naudoti šį raktą.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Šio rakto nesaugome, todėl išsaugokite šį 24 žodžių raktą saugioje vietoje.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Daryti tai vėliau'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Išsaugoti raktą'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Atkūrimo raktas išsaugotas atsisiuntimų aplanke.'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Neturite atkūrimo rakto?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Dvigubas tapatybės nustatymas'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Įveskite 6 skaitmenų kodą\niš autentifikatoriaus programos'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Prarastas įrenginys?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Įveskite atkūrimo raktą'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Atkurti'; - - @override - String get loggingOut => 'Atsijungiama...'; - - @override - String get immediately => 'Iš karto'; - - @override - String get appLock => 'Programos užraktas'; - - @override - String get autoLock => 'Automatinis užraktas'; - - @override - String get noSystemLockFound => 'Nerastas sistemos užraktas'; - - @override - String get deviceLockEnablePreSteps => - 'Kad įjungtumėte įrenginio užraktą, sistemos nustatymuose nustatykite įrenginio prieigos kodą arba ekrano užraktą.'; - - @override - String get appLockDescription => - 'Pasirinkite tarp numatytojo įrenginio užrakinimo ekrano ir pasirinktinio užrakinimo ekrano su PIN kodu arba slaptažodžiu.'; - - @override - String get deviceLock => 'Įrenginio užraktas'; - - @override - String get pinLock => 'PIN užraktas'; - - @override - String get autoLockFeatureDescription => - 'Laikas, po kurio programa užrakinama perkėlus ją į foną.'; - - @override - String get hideContent => 'Slėpti turinį'; - - @override - String get hideContentDescriptionAndroid => - 'Paslepia programų turinį programų perjungiklyje ir išjungia ekrano kopijas.'; - - @override - String get hideContentDescriptioniOS => - 'Paslepia programos turinį programos perjungiklyje.'; - - @override - String get tooManyIncorrectAttempts => 'Per daug neteisingų bandymų.'; - - @override - String get tapToUnlock => 'Palieskite, kad atrakintumėte'; - - @override - String get areYouSureYouWantToLogout => 'Ar tikrai norite atsijungti?'; - - @override - String get yesLogout => 'Taip, atsijungti'; - - @override - String get authToViewSecrets => - 'Nustatykite tapatybę, kad peržiūrėtumėte savo paslaptis'; - - @override - String get next => 'Toliau'; - - @override - String get setNewPassword => 'Nustatykite naują slaptažodį'; - - @override - String get enterPin => 'Įveskite PIN'; - - @override - String get setNewPin => 'Nustatykite naują PIN'; - - @override - String get confirm => 'Patvirtinti'; - - @override - String get reEnterPassword => 'Įveskite slaptažodį iš naujo'; - - @override - String get reEnterPin => 'Įveskite PIN iš naujo'; - - @override - String get androidBiometricHint => 'Patvirtinkite tapatybę'; - - @override - String get androidBiometricNotRecognized => - 'Neatpažinta. Bandykite dar kartą.'; - - @override - String get androidBiometricSuccess => 'Sėkmė'; - - @override - String get androidCancelButton => 'Atšaukti'; - - @override - String get androidSignInTitle => 'Privalomas tapatybės nustatymas'; - - @override - String get androidBiometricRequiredTitle => 'Privaloma biometrija'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Privalomi įrenginio kredencialai'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Privalomi įrenginio kredencialai'; - - @override - String get goToSettings => 'Eiti į nustatymus'; - - @override - String get androidGoToSettingsDescription => - 'Biometrinis tapatybės nustatymas jūsų įrenginyje nenustatytas. Eikite į Nustatymai > Saugumas ir pridėkite biometrinį tapatybės nustatymą.'; - - @override - String get iOSLockOut => - 'Biometrinis tapatybės nustatymas išjungtas. Kad jį įjungtumėte, užrakinkite ir atrakinkite ekraną.'; - - @override - String get iOSOkButton => 'Gerai'; - - @override - String get emailAlreadyRegistered => 'El. paštas jau užregistruotas.'; - - @override - String get emailNotRegistered => 'El. paštas neregistruotas.'; - - @override - String get thisEmailIsAlreadyInUse => 'Šis el. paštas jau naudojamas.'; - - @override - String emailChangedTo(String newEmail) { - return 'El. paštas pakeistas į $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Tapatybės nustatymas nepavyko. Bandykite dar kartą.'; - - @override - String get authenticationSuccessful => 'Tapatybės nustatymas sėkmingas.'; - - @override - String get sessionExpired => 'Seansas baigėsi'; - - @override - String get incorrectRecoveryKey => 'Neteisingas atkūrimo raktas'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Įvestas atkūrimo raktas yra neteisingas.'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Dvigubas tapatybės nustatymas sėkmingai iš naujo nustatytas.'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Jūsų patvirtinimo kodas nebegaliojantis.'; - - @override - String get incorrectCode => 'Neteisingas kodas'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Atsiprašome, įvestas kodas yra neteisingas.'; - - @override - String get developerSettings => 'Kūrėjo nustatymai'; - - @override - String get serverEndpoint => 'Serverio galutinis taškas'; - - @override - String get invalidEndpoint => 'Netinkamas galutinis taškas'; - - @override - String get invalidEndpointMessage => - 'Atsiprašome, įvestas galutinis taškas netinkamas. Įveskite tinkamą galutinį tašką ir bandykite dar kartą.'; - - @override - String get endpointUpdatedMessage => 'Galutinis taškas sėkmingai atnaujintas'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 7fb99652a5..1ec97207d9 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -103,530 +103,333 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Wil je dit opslaan naar je opslagruimte (Downloads map)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Voer uw nieuwe e-mailadres in'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'E-mail'; + String get email => 'Email'; @override - String get verify => 'Verifiëren'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Ongeldig e-mailadres'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Voer een geldig e-mailadres in.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Een ogenblik geduld...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Bevestig wachtwoord'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Onjuist wachtwoord'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Probeer het nog eens'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Voer wachtwoord in'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Voer je wachtwoord in'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Actieve sessies'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Oeps'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Er is iets fout gegaan, probeer het opnieuw'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Dit zal je uitloggen van dit apparaat!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Dit zal je uitloggen van het volgende apparaat:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Sessie beëindigen?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Beëindigen'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Dit apparaat'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Account aanmaken'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Zwak'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Matig'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Sterk'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Account verwijderen'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'We zullen het vervelend vinden om je te zien vertrekken. Zijn er problemen?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ja, geef feedback'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Nee, verwijder account'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Gelieve te verifiëren om het account te verwijderen'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Account verwijderen bevestigen'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Dit account is gekoppeld aan andere Ente apps, als je er gebruik van maakt.\n\nJe geüploade gegevens worden in alle Ente apps gepland voor verwijdering, en je account wordt permanent verwijderd voor alle Ente diensten.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Verwijderen'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Nieuw account aanmaken'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Wachtwoord'; + String get password => 'Password'; @override - String get confirmPassword => 'Wachtwoord bevestigen'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Wachtwoord sterkte: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Hoe hoorde je over Ente? (optioneel)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Wij gebruiken geen tracking. Het zou helpen als je ons vertelt waar je ons gevonden hebt!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Voorwaarden'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Privacybeleid'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Ik begrijp dat als ik mijn wachtwoord verlies, ik mijn gegevens kan verliezen omdat mijn gegevens end-to-end versleuteld zijn.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Encryptie'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Inloggen'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Welkom terug!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Door op inloggen te klikken, ga ik akkoord met de gebruiksvoorwaarden en privacybeleid'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Geen internetverbinding'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Controleer je internetverbinding en probeer het opnieuw.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Verificatie mislukt, probeer het opnieuw'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Wachtwoord opnieuw instellen'; + String get recreatePasswordTitle => 'Recreate password'; @override String get 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 herstelsleutel en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken).'; + '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).'; @override - String get useRecoveryKey => 'Herstelsleutel gebruiken'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Wachtwoord vergeten'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'E-mailadres wijzigen'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Bevestig e-mail'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'We hebben een e-mail gestuurd naar $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Verifieer eerst je e-mailadres om je wachtwoord opnieuw in te stellen.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Controleer je inbox (en spam) om verificatie te voltooien'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Tik om code in te voeren'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'E-mail versturen'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'E-mail opnieuw versturen'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Verificatie is nog in behandeling'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Sessie verlopen'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Jouw sessie is verlopen. Log opnieuw in.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Passkey verificatie'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Wachten op verificatie...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Probeer opnieuw'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Status controleren'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Inloggen met TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Account herstellen'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Wachtwoord instellen'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Wachtwoord wijzigen'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Wachtwoord resetten'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Encryptiesleutels'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Voer een wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Voer een nieuw wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Wij slaan dit wachtwoord niet op, dus als je het vergeet, kunnen we jouw gegevens niet ontsleutelen'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Hoe het werkt'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => - 'Encryptiesleutels worden gegenereerd...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Wachtwoord succesvol aangepast'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Afmelden bij andere apparaten'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Als je denkt dat iemand je wachtwoord zou kunnen kennen, kun je alle andere apparaten die je account gebruiken dwingen om uit te loggen.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Afmelden bij andere apparaten'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Niet uitloggen'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Encryptiesleutels genereren...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Doorgaan'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Onveilig apparaat'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we konden geen beveiligde sleutels genereren op dit apparaat.\n\nMeld je aan vanaf een ander apparaat.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Herstelsleutel gekopieerd naar klembord'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Herstelsleutel'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Als je je wachtwoord vergeet, kun je alleen met deze code je gegevens herstellen.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'We slaan deze code niet op, bewaar deze code met 24 woorden op een veilige plaats.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Doe dit later'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Sleutel opslaan'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Herstelsleutel opgeslagen in de Downloads map!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Geen herstelsleutel?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Tweestapsverificatie'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Voer de 6-cijferige code van je verificatie-app in'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Apparaat verloren?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Voer je herstelsleutel in'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Herstellen'; - - @override - String get loggingOut => 'Bezig met uitloggen...'; - - @override - String get immediately => 'Onmiddellijk'; - - @override - String get appLock => 'App-vergrendeling'; - - @override - String get autoLock => 'Automatische vergrendeling'; - - @override - String get noSystemLockFound => 'Geen systeemvergrendeling gevonden'; - - @override - String get deviceLockEnablePreSteps => - 'Om toestelvergrendeling in te schakelen, stelt u de toegangscode van het apparaat of schermvergrendeling in uw systeeminstellingen in.'; - - @override - String get appLockDescription => - 'Kies tussen de standaard schermvergrendeling van uw apparaat en een aangepaste schermvergrendeling met een pincode of wachtwoord.'; - - @override - String get deviceLock => 'Apparaat vergrendeling'; - - @override - String get pinLock => 'Pin vergrendeling'; - - @override - String get autoLockFeatureDescription => - 'Tijd waarna de app vergrendelt nadat ze op de achtergrond is gezet'; - - @override - String get hideContent => 'Inhoud verbergen'; - - @override - String get hideContentDescriptionAndroid => - 'Verbergt de app inhoud in de app switcher en schakelt schermafbeeldingen uit'; - - @override - String get hideContentDescriptioniOS => - 'Verbergt de inhoud van de app in de app switcher'; - - @override - String get tooManyIncorrectAttempts => 'Te veel onjuiste pogingen'; - - @override - String get tapToUnlock => 'Tik om te ontgrendelen'; - - @override - String get areYouSureYouWantToLogout => - 'Weet je zeker dat je wilt uitloggen?'; - - @override - String get yesLogout => 'Ja, uitloggen'; - - @override - String get authToViewSecrets => - 'Graag verifiëren om uw herstelsleutel te bekijken'; - - @override - String get next => 'Volgende'; - - @override - String get setNewPassword => 'Nieuw wachtwoord instellen'; - - @override - String get enterPin => 'Pin invoeren'; - - @override - String get setNewPin => 'Nieuwe pin instellen'; - - @override - String get confirm => 'Bevestig'; - - @override - String get reEnterPassword => 'Wachtwoord opnieuw invoeren'; - - @override - String get reEnterPin => 'PIN opnieuw invoeren'; - - @override - String get androidBiometricHint => 'Identiteit verifiëren'; - - @override - String get androidBiometricNotRecognized => - 'Niet herkend. Probeer het opnieuw.'; - - @override - String get androidBiometricSuccess => 'Succes'; - - @override - String get androidCancelButton => 'Annuleren'; - - @override - String get androidSignInTitle => 'Verificatie vereist'; - - @override - String get androidBiometricRequiredTitle => - 'Biometrische verificatie vereist'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Apparaatgegevens vereist'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Apparaatgegevens vereist'; - - @override - String get goToSettings => 'Ga naar instellingen'; - - @override - String get androidGoToSettingsDescription => - 'Biometrische verificatie is niet ingesteld op uw apparaat. Ga naar \'Instellingen > Beveiliging\' om biometrische verificatie toe te voegen.'; - - @override - String get iOSLockOut => - 'Biometrische verificatie is uitgeschakeld. Vergrendel en ontgrendel uw scherm om het in te schakelen.'; - - @override - String get iOSOkButton => 'Oké'; - - @override - String get emailAlreadyRegistered => 'E-mail is al geregistreerd.'; - - @override - String get emailNotRegistered => 'E-mail niet geregistreerd.'; - - @override - String get thisEmailIsAlreadyInUse => 'Dit e-mailadres is al in gebruik'; - - @override - String emailChangedTo(String newEmail) { - return 'E-mailadres gewijzigd naar $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Verificatie mislukt, probeer het opnieuw'; - - @override - String get authenticationSuccessful => 'Verificatie geslaagd!'; - - @override - String get sessionExpired => 'Sessie verlopen'; - - @override - String get incorrectRecoveryKey => 'Onjuiste herstelsleutel'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'De ingevoerde herstelsleutel is onjuist'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Tweestapsverificatie succesvol gereset'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => 'Uw verificatiecode is verlopen'; - - @override - String get incorrectCode => 'Onjuiste code'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Sorry, de ingevoerde code is onjuist'; - - @override - String get developerSettings => 'Ontwikkelaarsinstellingen'; - - @override - String get serverEndpoint => 'Server eindpunt'; - - @override - String get invalidEndpoint => 'Ongeldig eindpunt'; - - @override - String get invalidEndpointMessage => - 'Sorry, het eindpunt dat u hebt ingevoerd is ongeldig. Voer een geldig eindpunt in en probeer het opnieuw.'; - - @override - String get endpointUpdatedMessage => 'Eindpunt met succes bijgewerkt'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index 5d636efe39..adf63c13c7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -103,528 +103,333 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Wprowadź nowy adres e-mail'; + String get enterNewEmailHint => 'Enter your new email address'; @override String get email => 'Email'; @override - String get verify => 'Zweryfikuj'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Nieprawidłowy adres e-mail'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Prosimy podać prawidłowy adres e-mail.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Prosimy czekać...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Zweryfikuj hasło'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Nieprawidłowe hasło'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Prosimy spróbować ponownie'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Wprowadź hasło'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Wprowadź swoje hasło'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktywne sesje'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Coś poszło nie tak, spróbuj ponownie'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'To wyloguje Cię z tego urządzenia!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'To wyloguje Cię z tego urządzenia:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Zakończyć sesję?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Zakończ'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'To urządzenie'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Utwórz konto'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Słabe'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Umiarkowane'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Silne'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Usuń konto'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Będzie nam przykro, że odchodzisz. Masz jakiś problem?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Tak, wyślij opinię'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Nie, usuń moje konto'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Prosimy uwierzytelnić się, aby zainicjować usuwanie konta'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Potwierdź usunięcie konta'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'To konto jest połączone z innymi aplikacjami Ente, jeśli ich używasz.\n\nTwoje przesłane dane, we wszystkich aplikacjach Ente, zostaną zaplanowane do usunięcia, a Twoje konto zostanie trwale usunięte.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Usuń'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Utwórz nowe konto'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Hasło'; + String get password => 'Password'; @override - String get confirmPassword => 'Potwierdź hasło'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Siła hasła: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Jak usłyszałeś/aś o Ente? (opcjonalnie)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Akceptuję warunki korzystania z usługi i politykę prywatności'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Regulamin'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Polityka prywatności'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Rozumiem, że jeśli utracę hasło, mogę stracić moje dane, ponieważ moje dane są szyfrowane end-to-end.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Szyfrowanie'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Zaloguj się'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Witaj ponownie!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Klikając, zaloguj się, zgadzam się na regulamin i politykę prywatności'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Brak połączenia z Internetem'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Prosimy sprawdzić połączenie internetowe i spróbować ponownie.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Weryfikacja nie powiodła się, spróbuj ponownie'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Zresetuj hasło'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Obecne urządzenie nie jest wystarczająco wydajne, aby zweryfikować Twoje hasło, więc musimy je raz zregenerować w sposób, który działa ze wszystkimi urządzeniami. \n\nZaloguj się przy użyciu klucza odzyskiwania i zresetuj swoje hasło (możesz ponownie użyć tego samego, jeśli chcesz).'; + '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).'; @override - String get useRecoveryKey => 'Użyj kodu odzyskiwania'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Nie pamiętam hasła'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Zmień adres e-mail'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Zweryfikuj adres e-mail'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Wysłaliśmy wiadomość do $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Aby zresetować hasło, najpierw zweryfikuj swój e-mail.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Dotknij, aby wprowadzić kod'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Wyślij e-mail'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Wyślij e-mail ponownie'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Weryfikacja jest nadal w toku'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Sesja wygasła'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Twoja sesja wygasła. Zaloguj się ponownie.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Weryfikacja kluczem dostępu'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Oczekiwanie na weryfikację...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Spróbuj ponownie'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Sprawdź stan'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Zaloguj się za pomocą TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Odzyskaj konto'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Ustaw hasło'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Zmień hasło'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Zresetuj hasło'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Klucz szyfrowania'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Wprowadź hasło, którego możemy użyć do zaszyfrowania Twoich danych'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Wprowadź nowe hasło, którego możemy użyć do zaszyfrowania Twoich danych'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Nie przechowujemy tego hasła, więc jeśli go zapomnisz, nie będziemy w stanie odszyfrować Twoich danych'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Jak to działa'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Generowanie kluczy szyfrujących...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Hasło zostało pomyślnie zmienione'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Wyloguj z pozostałych urządzeń'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Jeśli uważasz, że ktoś może znać Twoje hasło, możesz wymusić wylogowanie na wszystkich innych urządzeniach korzystających z Twojego konta.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Wyloguj z pozostałych urządzeń'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Nie wylogowuj mnie'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Generowanie kluczy szyfrujących...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Kontynuuj'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Niezabezpieczone urządzenie'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Przepraszamy, nie mogliśmy wygenerować kluczy bezpiecznych na tym urządzeniu.\n\nZarejestruj się z innego urządzenia.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Klucz odzyskiwania został skopiowany do schowka'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Klucz odzyskiwania'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Jeśli zapomnisz hasła, jedynym sposobem na odzyskanie danych jest ten klucz.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Nie przechowujemy tego klucza, prosimy zachować ten 24-słowny klucz w bezpiecznym miejscu.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Zrób to później'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Zapisz klucz'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Klucz odzyskiwania zapisany w folderze Pobrane!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Brak klucza odzyskiwania?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Uwierzytelnianie dwustopniowe'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Wprowadź sześciocyfrowy kod z \nTwojej aplikacji uwierzytelniającej'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Zagubiono urządzenie?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Wprowadź swój klucz odzyskiwania'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Odzyskaj'; - - @override - String get loggingOut => 'Wylogowywanie...'; - - @override - String get immediately => 'Natychmiast'; - - @override - String get appLock => 'Blokada aplikacji'; - - @override - String get autoLock => 'Automatyczna blokada'; - - @override - String get noSystemLockFound => 'Nie znaleziono blokady systemowej'; - - @override - String get deviceLockEnablePreSteps => - 'Aby włączyć blokadę aplikacji, należy skonfigurować hasło urządzenia lub blokadę ekranu w ustawieniach Twojego systemu.'; - - @override - String get appLockDescription => - 'Wybierz między domyślnym ekranem blokady urządzenia a niestandardowym ekranem blokady z kodem PIN lub hasłem.'; - - @override - String get deviceLock => 'Blokada urządzenia'; - - @override - String get pinLock => 'Blokada PIN'; - - @override - String get autoLockFeatureDescription => - 'Czas, po którym aplikacja blokuje się po umieszczeniu jej w tle'; - - @override - String get hideContent => 'Ukryj zawartość'; - - @override - String get hideContentDescriptionAndroid => - 'Ukrywa zawartość aplikacji w przełączniku aplikacji i wyłącza zrzuty ekranu'; - - @override - String get hideContentDescriptioniOS => - 'Ukrywa zawartość aplikacji w przełączniku aplikacji'; - - @override - String get tooManyIncorrectAttempts => 'Zbyt wiele błędnych prób'; - - @override - String get tapToUnlock => 'Naciśnij, aby odblokować'; - - @override - String get areYouSureYouWantToLogout => 'Czy na pewno chcesz się wylogować?'; - - @override - String get yesLogout => 'Tak, wyloguj'; - - @override - String get authToViewSecrets => - 'Prosimy uwierzytelnić się, aby wyświetlić swoje sekrety'; - - @override - String get next => 'Dalej'; - - @override - String get setNewPassword => 'Ustaw nowe hasło'; - - @override - String get enterPin => 'Wprowadź kod PIN'; - - @override - String get setNewPin => 'Ustaw nowy kod PIN'; - - @override - String get confirm => 'Potwierdź'; - - @override - String get reEnterPassword => 'Wprowadź ponownie hasło'; - - @override - String get reEnterPin => 'Wprowadź ponownie kod PIN'; - - @override - String get androidBiometricHint => 'Potwierdź swoją tożsamość'; - - @override - String get androidBiometricNotRecognized => - 'Nie rozpoznano. Spróbuj ponownie.'; - - @override - String get androidBiometricSuccess => 'Sukces'; - - @override - String get androidCancelButton => 'Anuluj'; - - @override - String get androidSignInTitle => 'Wymagana autoryzacja'; - - @override - String get androidBiometricRequiredTitle => 'Wymagana biometria'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Wymagane dane logowania urządzenia'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Wymagane dane logowania urządzenia'; - - @override - String get goToSettings => 'Przejdź do ustawień'; - - @override - String get androidGoToSettingsDescription => - 'Uwierzytelnianie biometryczne nie jest skonfigurowane na tym urządzeniu. Przejdź do \'Ustawienia > Bezpieczeństwo\', aby dodać uwierzytelnianie biometryczne.'; - - @override - String get iOSLockOut => - 'Uwierzytelnianie biometryczne jest wyłączone. Prosimy zablokować i odblokować ekran, aby je włączyć.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'Adres e-mail jest już zarejestrowany.'; - - @override - String get emailNotRegistered => 'Adres e-mail nie jest zarejestrowany.'; - - @override - String get thisEmailIsAlreadyInUse => 'Ten adres e-mail już jest zajęty'; - - @override - String emailChangedTo(String newEmail) { - return 'Adres e-mail został zmieniony na $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Uwierzytelnianie nie powiodło się, prosimy spróbować ponownie'; - - @override - String get authenticationSuccessful => 'Uwierzytelnianie powiodło się!'; - - @override - String get sessionExpired => 'Sesja wygasła'; - - @override - String get incorrectRecoveryKey => 'Nieprawidłowy klucz odzyskiwania'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Wprowadzony klucz odzyskiwania jest nieprawidłowy'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Pomyślnie zresetowano uwierzytelnianie dwustopniowe'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => 'Twój kod weryfikacyjny wygasł'; - - @override - String get incorrectCode => 'Nieprawidłowy kod'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Niestety, wprowadzony kod jest nieprawidłowy'; - - @override - String get developerSettings => 'Ustawienia dla programistów'; - - @override - String get serverEndpoint => 'Punkt końcowy serwera'; - - @override - String get invalidEndpoint => 'Punkt końcowy jest nieprawidłowy'; - - @override - String get invalidEndpointMessage => - 'Niestety, wprowadzony punkt końcowy jest nieprawidłowy. Wprowadź prawidłowy punkt końcowy i spróbuj ponownie.'; - - @override - String get endpointUpdatedMessage => 'Punkt końcowy zaktualizowany pomyślnie'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index b339c4f481..13074ce5e8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -103,527 +103,333 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get saveOnlyDescription => - 'Deseja mesmo salvar em seu armazenamento (pasta de Downloads por padrão)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Insira seu novo e-mail'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'E-mail'; + String get email => 'Email'; @override - String get verify => 'Verificar'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Endereço de e-mail inválido'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Insira um endereço de e-mail válido.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Aguarde...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Verificar senha'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Senha incorreta'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Tente novamente'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Inserir senha'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Insira sua senha'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Sessões ativas'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Opa'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Algo deu errado. Tente outra vez'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Isso fará com que você saia deste dispositivo!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Isso fará você sair do dispositivo a seguir:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Sair?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Encerrar'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Esse dispositivo'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Criar conta'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Fraca'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Moderada'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Forte'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Excluir conta'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Estamos tristes por vê-lo sair. Você enfrentou algum problema?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Sim, enviar feedback'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Não, excluir conta'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Autentique-se para iniciar a exclusão de conta'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Confirmar exclusão de conta'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Esta conta está vinculada a outros apps Ente, se você usa algum.\n\nSeus dados enviados, entre todos os apps Ente, serão marcados para exclusão, e sua conta será apagada permanentemente.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Excluir'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Criar nova conta'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Senha'; + String get password => 'Password'; @override - String get confirmPassword => 'Confirmar senha'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Força da senha: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Como você descobriu o Ente? (opcional)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Não rastreamos instalações. Ajudaria bastante se você contasse onde nos achou!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Eu concordo com os termos de serviço e a política de privacidade'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Termos'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Política de Privacidade'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Eu entendo que se eu perder minha senha, posso perder meus dados, já que meus dados são criptografados de ponta a ponta.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Criptografia'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Entrar'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Bem-vindo(a) de volta!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Ao clicar em iniciar sessão, eu concordo com os termos de serviço e a política de privacidade'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Não conectado à internet'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Verifique sua conexão com a internet e tente novamente.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Falhou na verificação. Tente novamente'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Redefinir senha'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Não é possível verificar a sua senha no dispositivo atual, mas podemos regenerá-la para que funcione em todos os dispositivos. \n\nEntre com a sua chave de recuperação e regenere sua senha (você pode usar a mesma se quiser).'; + '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).'; @override - String get useRecoveryKey => 'Usar chave de recuperação'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Esqueci a senha'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Alterar e-mail'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Verificar e-mail'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Enviamos um e-mail à $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Para redefinir sua senha, verifique seu e-mail primeiramente.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Verifique sua caixa de entrada (e spam) para concluir a verificação'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Toque para inserir código'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Enviar e-mail'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Reenviar e-mail'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'A verificação ainda está pendente'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Sessão expirada'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Sua sessão expirou. Registre-se novamente.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Verificação de chave de acesso'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Aguardando verificação...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Tente novamente'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Verificar status'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Registrar com TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Recuperar conta'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Definir senha'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Alterar senha'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Redefinir senha'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Chaves de criptografia'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Insira uma senha que podemos usar para criptografar seus dados'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Insira uma nova senha para criptografar seus dados'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Não salvamos esta senha, então se você esquecê-la, não podemos descriptografar seus dados'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Como funciona'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Gerando chaves de criptografia...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'A senha foi alterada'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Sair da conta em outros dispositivos'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Se você acha que alguém possa saber da sua senha, você pode forçar desconectar sua conta de outros dispositivos.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Sair em outros dispositivos'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Não sair'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Gerando chaves de criptografia...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Continuar'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Dispositivo inseguro'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Desculpe, não foi possível gerar chaves de segurança nesse dispositivo.\n\ninicie sessão em um dispositivo diferente.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Chave de recuperação copiada para a área de transferência'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Chave de recuperação'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Caso esqueça sua senha, a única maneira de recuperar seus dados é com esta chave.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Não armazenamos esta chave de 24 palavras. Salve-a em um lugar seguro.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Fazer isso depois'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Salvar chave'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Chave de recuperação salva na pasta Downloads!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Sem chave de recuperação?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Autenticação de dois fatores'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Insira o código de 6 dígitos do aplicativo autenticador'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Perdeu o dispositivo?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Digite a chave de recuperação'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Recuperar'; - - @override - String get loggingOut => 'Desconectando...'; - - @override - String get immediately => 'Imediatamente'; - - @override - String get appLock => 'Bloqueio do aplicativo'; - - @override - String get autoLock => 'Bloqueio automático'; - - @override - String get noSystemLockFound => 'Nenhum bloqueio do sistema encontrado'; - - @override - String get deviceLockEnablePreSteps => - 'Para ativar o bloqueio do dispositivo, configure a senha do dispositivo ou o bloqueio de tela nas configurações do seu sistema.'; - - @override - String get appLockDescription => - 'Escolha entre a tela de bloqueio padrão do seu dispositivo e uma tela de bloqueio personalizada com PIN ou senha.'; - - @override - String get deviceLock => 'Bloqueio do dispositivo'; - - @override - String get pinLock => 'PIN de bloqueio'; - - @override - String get autoLockFeatureDescription => - 'Tempo de bloqueio do aplicativo em segundo plano'; - - @override - String get hideContent => 'Ocultar conteúdo'; - - @override - String get hideContentDescriptionAndroid => - 'Oculta o conteúdo do aplicativo no seletor de aplicativos e desativa as capturas de tela'; - - @override - String get hideContentDescriptioniOS => - 'Oculta o conteúdo do seletor de aplicativos'; - - @override - String get tooManyIncorrectAttempts => 'Muitas tentativas incorretas'; - - @override - String get tapToUnlock => 'Toque para desbloquear'; - - @override - String get areYouSureYouWantToLogout => 'Deseja mesmo sair?'; - - @override - String get yesLogout => 'Sim, quero sair'; - - @override - String get authToViewSecrets => 'Autentique-se para ver suas chaves secretas'; - - @override - String get next => 'Avançar'; - - @override - String get setNewPassword => 'Defina a nova senha'; - - @override - String get enterPin => 'Inserir PIN'; - - @override - String get setNewPin => 'Definir novo PIN'; - - @override - String get confirm => 'Confirmar'; - - @override - String get reEnterPassword => 'Reinserir senha'; - - @override - String get reEnterPin => 'Reinserir PIN'; - - @override - String get androidBiometricHint => 'Verificar identidade'; - - @override - String get androidBiometricNotRecognized => 'Não reconhecido. Tente de novo.'; - - @override - String get androidBiometricSuccess => 'Sucesso'; - - @override - String get androidCancelButton => 'Cancelar'; - - @override - String get androidSignInTitle => 'Autenticação necessária'; - - @override - String get androidBiometricRequiredTitle => 'Biometria necessária'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Credenciais necessários do dispositivo'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Credenciais necessários do dispositivo'; - - @override - String get goToSettings => 'Ir para Opções'; - - @override - String get androidGoToSettingsDescription => - 'A autenticação biométrica não está configurada no seu dispositivo. Vá em \'Configurações > Segurança\' para adicionar a autenticação biométrica.'; - - @override - String get iOSLockOut => - 'A autenticação biométrica está desativada. Bloqueie e desbloqueie sua tela para ativá-la.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'E-mail já registrado.'; - - @override - String get emailNotRegistered => 'E-mail não registrado.'; - - @override - String get thisEmailIsAlreadyInUse => 'Este e-mail já está em uso'; - - @override - String emailChangedTo(String newEmail) { - return 'E-mail alterado para $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'A autenticação falhou. Tente novamente'; - - @override - String get authenticationSuccessful => 'Autenticado!'; - - @override - String get sessionExpired => 'Sessão expirada'; - - @override - String get incorrectRecoveryKey => 'Chave de recuperação incorreta'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'A chave de recuperação inserida está incorreta'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Autenticação de dois fatores redefinida com sucesso'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Seu código de verificação expirou'; - - @override - String get incorrectCode => 'Código incorreto'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'O código inserido está incorreto'; - - @override - String get developerSettings => 'Opções de Desenvolvedor'; - - @override - String get serverEndpoint => 'Endpoint do servidor'; - - @override - String get invalidEndpoint => 'Endpoint inválido'; - - @override - String get invalidEndpointMessage => - 'Desculpe, o ponto de acesso inserido é inválido. Insira um ponto de acesso válido e tente novamente.'; - - @override - String get endpointUpdatedMessage => 'O endpoint foi atualizado'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index 52dfd4e049..3f48688420 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -103,530 +103,333 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get saveOnlyDescription => - 'Вы хотите сохранить это в хранилище (по умолчанию папка загрузок)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Введите ваш новый адрес электронной почты'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Электронная почта'; + String get email => 'Email'; @override - String get verify => 'Подтвердить'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Неверный адрес электронной почты'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => - 'Пожалуйста, введите действительный адрес электронной почты.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Пожалуйста, подождите...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Подтверждение пароля'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Неправильный пароль'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Пожалуйста, попробуйте ещё раз'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Введите пароль'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Введите пароль'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Активные сеансы'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ой'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Что-то пошло не так. Попробуйте еще раз'; + 'Something went wrong, please try again'; @override - String get thisWillLogYouOutOfThisDevice => 'Вы выйдете из этого устройства!'; + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Вы выйдете из списка следующих устройств:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Завершить сеанс?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Завершить'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Это устройство'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Создать аккаунт'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Слабый'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Средний'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Сильный'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Удалить аккаунт'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Нам будет жаль, если вы уйдете. Вы столкнулись с какой-то проблемой?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Да, отправить отзыв'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Нет, удалить аккаунт'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Пожалуйста, авторизуйтесь, чтобы начать удаление аккаунта'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Подтвердить удаление аккаунта'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Эта учетная запись связана с другими приложениями Ente, если вы ими пользуетесь.\n\nЗагруженные вами данные во всех приложениях Ente будут запланированы к удалению, а ваша учетная запись будет удалена без возможности восстановления.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Удалить'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Создать новый аккаунт'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Пароль'; + String get password => 'Password'; @override - String get confirmPassword => 'Подтвердить пароль'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Мощность пароля: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Как вы узнали о Ente? (необязательно)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Мы не отслеживаем установки приложений. Было бы полезно, если бы вы сказали, где нас нашли!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Я согласен с условиями предоставления услуг и политикой конфиденциальности'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Условия использования'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Политика конфиденциальности'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Я понимаю, что если я потеряю свой пароль, я могу потерять свои данные, так как мои данные в сквозном шифровании.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Шифрование'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Войти'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'С возвращением!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Нажимая на логин, я принимаю условия использования и политику конфиденциальности'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Нет подключения к Интернету'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Проверьте подключение к Интернету и повторите попытку.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Проверка не удалась, попробуйте еще раз'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Пересоздать пароль'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Текущее устройство недостаточно мощно для верификации пароля, но мы можем регенерировать так, как это работает со всеми устройствами.\n\nПожалуйста, войдите, используя ваш ключ восстановления и сгенерируйте ваш пароль (вы можете использовать тот же пароль, если пожелаете).'; + '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).'; @override - String get useRecoveryKey => 'Использовать ключ восстановления'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Забыл пароль'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Изменить адрес электронной почты'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Подтвердить адрес электронной почты'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Мы отправили письмо на $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Подтвердите адрес электронной почты, чтобы сбросить пароль.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Пожалуйста, проверьте свой почтовый ящик (и спам) для завершения верификации'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Нажмите, чтобы ввести код'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Отправить электронное письмо'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Отправить письмо еще раз'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Верификация еще не завершена'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Сессия недействительна'; + String get loginSessionExpired => 'Session expired'; @override - String get loginSessionExpiredDetails => 'Сессия истекла. Войдите снова.'; + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Проверка с помощью ключа доступа'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Ожидание подтверждения...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Попробовать снова'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Проверить статус'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Войти с помощью TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Восстановить аккаунт'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Поставить пароль'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Изменить пароль'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Сбросить пароль'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Ключи шифрования'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Введите пароль, который мы можем использовать для шифрования ваших данных'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Введите новый пароль, который мы можем использовать для шифрования ваших данных'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Мы не храним этот пароль, поэтому если вы забудете его, мы не сможем расшифровать ваши данные'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Как это работает'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Генерируем ключи шифрования...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Пароль успешно изменён'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Выйти из других устройств'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Если вы думаете, что кто-то может знать ваш пароль, вы можете принудительно выйти из всех устройств.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Выйти из других устройств'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Не выходить'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Генерируем ключи шифрования...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Далее'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Небезопасное устройство'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'К сожалению, мы не смогли сгенерировать безопасные ключи на этом устройстве.\n\nПожалуйста, зарегистрируйтесь с другого устройства.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Ключ восстановления скопирован в буфер обмена'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Ключ восстановления'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Если вы забыли свой пароль, то восстановить данные можно только с помощью этого ключа.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Мы не храним этот ключ, пожалуйста, сохраните этот ключ в безопасном месте.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Сделать позже'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Сохранить ключ'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Ключ восстановления сохранён в папке Загрузки!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Нет ключа восстановления?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Двухфакторная аутентификация'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Введите 6-значный код из\nвашего приложения-аутентификатора'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Потеряно устройство?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Введите ключ восстановления'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Восстановить'; - - @override - String get loggingOut => 'Выходим...'; - - @override - String get immediately => 'Немедленно'; - - @override - String get appLock => 'Блокировка приложения'; - - @override - String get autoLock => 'Автоблокировка'; - - @override - String get noSystemLockFound => 'Системная блокировка не найдена'; - - @override - String get deviceLockEnablePreSteps => - 'Чтобы включить блокировку устройства, пожалуйста, настройте пароль или блокировку экрана в настройках системы.'; - - @override - String get appLockDescription => - 'Выберите между экраном блокировки вашего устройства и пользовательским экраном блокировки с PIN-кодом или паролем.'; - - @override - String get deviceLock => 'Блокировка устройства'; - - @override - String get pinLock => 'Pin блокировка'; - - @override - String get autoLockFeatureDescription => - 'Время в фоне, после которого приложение блокируется'; - - @override - String get hideContent => 'Скрыть содержимое'; - - @override - String get hideContentDescriptionAndroid => - 'Скрывает содержимое приложения в переключателе приложений и отключает скриншоты'; - - @override - String get hideContentDescriptioniOS => - 'Скрывает содержимое приложения в переключателе приложений'; - - @override - String get tooManyIncorrectAttempts => 'Слишком много неудачных попыток'; - - @override - String get tapToUnlock => 'Нажмите для разблокировки'; - - @override - String get areYouSureYouWantToLogout => 'Вы уверены, что хотите выйти?'; - - @override - String get yesLogout => 'Да, выйти'; - - @override - String get authToViewSecrets => - 'Пожалуйста, авторизуйтесь для просмотра ваших секретов'; - - @override - String get next => 'Далее'; - - @override - String get setNewPassword => 'Задать новый пароль'; - - @override - String get enterPin => 'Введите PIN'; - - @override - String get setNewPin => 'Установите новый PIN'; - - @override - String get confirm => 'Подтвердить'; - - @override - String get reEnterPassword => 'Подтвердите пароль'; - - @override - String get reEnterPin => 'Введите PIN-код ещё раз'; - - @override - String get androidBiometricHint => 'Подтвердите личность'; - - @override - String get androidBiometricNotRecognized => - 'Не распознано. Попробуйте еще раз.'; - - @override - String get androidBiometricSuccess => 'Успешно'; - - @override - String get androidCancelButton => 'Отменить'; - - @override - String get androidSignInTitle => 'Требуется аутентификация'; - - @override - String get androidBiometricRequiredTitle => 'Требуется биометрия'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Требуются учетные данные устройства'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Требуются учетные данные устройства'; - - @override - String get goToSettings => 'Перейдите к настройкам'; - - @override - String get androidGoToSettingsDescription => - 'Биометрическая аутентификация не настроена на вашем устройстве. Перейдите в \"Настройки > Безопасность\", чтобы добавить биометрическую аутентификацию.'; - - @override - String get iOSLockOut => - 'Биометрическая аутентификация отключена. Пожалуйста, заблокируйте и разблокируйте экран, чтобы включить ее.'; - - @override - String get iOSOkButton => 'ОК'; - - @override - String get emailAlreadyRegistered => - 'Адрес электронной почты уже зарегистрирован.'; - - @override - String get emailNotRegistered => - 'Адрес электронной почты не зарегистрирован.'; - - @override - String get thisEmailIsAlreadyInUse => - 'Этот адрес электронной почты уже используется'; - - @override - String emailChangedTo(String newEmail) { - return 'Адрес электронной почты изменен на $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Аутентификация не удалась, попробуйте еще раз'; - - @override - String get authenticationSuccessful => 'Аутентификация прошла успешно!'; - - @override - String get sessionExpired => 'Сеанс истек'; - - @override - String get incorrectRecoveryKey => 'Неправильный ключ восстановления'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Введен неправильный ключ восстановления'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Двухфакторная аутентификация успешно сброшена'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Срок действия вашего проверочного кода истек'; - - @override - String get incorrectCode => 'Неверный код'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Извините, введенный вами код неверный'; - - @override - String get developerSettings => 'Настройки разработчика'; - - @override - String get serverEndpoint => 'Конечная точка сервера'; - - @override - String get invalidEndpoint => 'Неверная конечная точка'; - - @override - String get invalidEndpointMessage => - 'Извините, введенная вами конечная точка неверна. Пожалуйста, введите корректную конечную точку и повторите попытку.'; - - @override - String get endpointUpdatedMessage => 'Конечная точка успешно обновлена'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 6ab32314df..8c96113796 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -103,7 +103,7 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get saveOnlyDescription => - 'Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,518 +112,324 @@ class StringsLocalizationsSk extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Overiť'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Neplatná emailová adresa'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Zadajte platnú e-mailovú adresu.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Prosím počkajte...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Potvrďte heslo'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Nesprávne heslo'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Prosím, skúste to znova'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Zadajte heslo'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Zadajte vaše heslo'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktívne relácie'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Ups'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Niečo sa pokazilo, skúste to prosím znova'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Toto vás odhlási z tohto zariadenia!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Toto vás odhlási z následujúceho zariadenia:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Ukončiť reláciu?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Ukončiť'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Toto zariadenie'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Vytvoriť účet'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Slabé'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Mierne'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Silné'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Odstrániť účet'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Bude nám ľúto ak odídeš. Máš nejaký problém?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Áno, odoslať spätnú väzbu'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Nie, odstrániť účet'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Je potrebné overenie pre spustenie odstránenia účtu'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Potvrď odstránenie účtu'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používaš.\n\nTvoje nahrané údaje vo všetkých Ente aplikáciách budú naplánované na odstránenie a tvoj účet bude natrvalo odstránený.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Odstrániť'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Vytvoriť nový účet'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Heslo'; + String get password => 'Password'; @override - String get confirmPassword => 'Potvrdiť heslo'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Sila hesla: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Ako ste sa dozvedeli o Ente? (voliteľné)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Nesledujeme inštalácie aplikácie. Veľmi by nám pomohlo, keby ste nám povedali, ako ste sa o nás dozvedeli!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Podmienky používania'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Zásady ochrany osobných údajov'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Rozumiem, že ak stratím alebo zabudnem heslo, môžem stratiť svoje údaje, pretože moje údaje sú šifrované end-to-end.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Šifrovanie'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Prihlásenie'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Vitajte späť!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Kliknutím na prihlásenie, súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Žiadne internetové pripojenie'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Skontrolujte svoje internetové pripojenie a skúste to znova.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Overenie zlyhalo, skúste to znova'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Resetovať heslo'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Aktuálne zariadenie nie je dostatočne výkonné na overenie vášho hesla, avšak vieme ho regenerovať spôsobom, ktorý funguje vo všetkých zariadeniach.\n\nPrihláste sa pomocou kľúča na obnovenie a znovu vygenerujte svoje heslo (ak si prajete, môžete znova použiť rovnaké).'; + '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).'; @override - String get useRecoveryKey => 'Použiť kľúč na obnovenie'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Zabudnuté heslo'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Zmeniť e-mail'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Overiť email'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Odoslali sme email na adresu $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Ak chcete obnoviť svoje heslo, najskôr overte svoj email.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Skontrolujte svoju doručenú poštu (a spam) pre dokončenie overenia'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Klepnutím zadajte kód'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Odoslať email'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Znovu odoslať email'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Overenie stále prebieha'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Relácia vypršala'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Vaša relácia vypršala. Prosím, prihláste sa znovu.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Overenie pomocou passkey'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Čakanie na overenie...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Skúsiť znova'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Overiť stav'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Prihlásenie pomocou TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Obnoviť účet'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Nastaviť heslo'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Zmeniť heslo'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Obnoviť heslo'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Šifrovacie kľúče'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Zadajte heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Zadajte nové heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Ente neukladá tohto heslo. V prípade, že ho zabudnete, nie sme schopní rozšifrovať vaše údaje'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Ako to funguje'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Generovanie šifrovacích kľúčov...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Heslo bolo úspešne zmenené'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Odhlásiť sa z iných zariadení'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Ak si myslíš, že by niekto mohol poznať tvoje heslo, môžeš vynútiť odhlásenie všetkých ostatných zariadení používajúcich tvoj účet.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Odhlásiť iné zariadenie'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Neodhlasovať'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Generovanie šifrovacích kľúčov...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Pokračovať'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Slabo zabezpečené zariadenie'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Ospravedlňujeme sa, v tomto zariadení sme nemohli generovať bezpečnostné kľúče.\n\nzaregistrujte sa z iného zariadenia.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Skopírovaný kód pre obnovenie do schránky'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Kľúč pre obnovenie'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Ak zabudnete heslo, jediným spôsobom, ako môžete obnoviť svoje údaje, je tento kľúč.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'My tento kľúč neuchovávame, uložte si tento kľúč obsahujúci 24 slov na bezpečnom mieste.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Urobiť to neskôr'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Uložiť kľúč'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Kľúč na obnovenie uložený v priečinku Stiahnutých súborov!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Nemáte kľúč pre obnovenie?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Dvojfaktorové overovanie'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Zadajte 6-miestny kód z\nvašej overovacej aplikácie'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Stratené zariadenie?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Vložte váš kód pre obnovenie'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Obnoviť'; - - @override - String get loggingOut => 'Odhlasovanie...'; - - @override - String get immediately => 'Okamžite'; - - @override - String get appLock => 'Zámok aplikácie'; - - @override - String get autoLock => 'Automatické uzamknutie'; - - @override - String get noSystemLockFound => 'Nenájdená žiadna zámka obrazovky'; - - @override - String get deviceLockEnablePreSteps => - 'Pre povolenie zámku zariadenia, nastavte prístupový kód zariadenia alebo zámok obrazovky v nastaveniach systému.'; - - @override - String get appLockDescription => - 'Vyberte si medzi predvolenou zámkou obrazovky vášho zariadenia a vlastnou zámkou obrazovky s PIN kódom alebo heslom.'; - - @override - String get deviceLock => 'Zámok zariadenia'; - - @override - String get pinLock => 'Zámok PIN'; - - @override - String get autoLockFeatureDescription => - 'Čas, po ktorom sa aplikácia uzamkne po nečinnosti'; - - @override - String get hideContent => 'Skryť obsah'; - - @override - String get hideContentDescriptionAndroid => - 'Skrýva obsah v prepínači aplikácii a zakazuje snímky obrazovky'; - - @override - String get hideContentDescriptioniOS => 'Skrýva obsah v prepínači aplikácii'; - - @override - String get tooManyIncorrectAttempts => 'Príliš veľa chybných pokusov'; - - @override - String get tapToUnlock => 'Ťuknutím odomknete'; - - @override - String get areYouSureYouWantToLogout => 'Naozaj sa chcete odhlásiť?'; - - @override - String get yesLogout => 'Áno, odhlásiť sa'; - - @override - String get authToViewSecrets => - 'Pre zobrazenie vašich tajných údajov sa musíte overiť'; - - @override - String get next => 'Ďalej'; - - @override - String get setNewPassword => 'Nastaviť nové heslo'; - - @override - String get enterPin => 'Zadajte PIN'; - - @override - String get setNewPin => 'Nastaviť nový PIN'; - - @override - String get confirm => 'Potvrdiť'; - - @override - String get reEnterPassword => 'Zadajte heslo znova'; - - @override - String get reEnterPin => 'Zadajte PIN znova'; - - @override - String get androidBiometricHint => 'Overiť identitu'; - - @override - String get androidBiometricNotRecognized => 'Nerozpoznané. Skúste znova.'; - - @override - String get androidBiometricSuccess => 'Overenie úspešné'; - - @override - String get androidCancelButton => 'Zrušiť'; - - @override - String get androidSignInTitle => 'Vyžaduje sa overenie'; - - @override - String get androidBiometricRequiredTitle => 'Vyžaduje sa biometria'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Vyžadujú sa poverenia zariadenia'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Vyžadujú sa poverenia zariadenia'; - - @override - String get goToSettings => 'Prejsť do nastavení'; - - @override - String get androidGoToSettingsDescription => - 'Overenie pomocou biometrie nie je na vašom zariadení nastavené. Prejdite na \'Nastavenie > Zabezpečenie\' a pridajte overenie pomocou biometrie.'; - - @override - String get iOSLockOut => - 'Overenie pomocou biometrie je zakázané. Zamknite a odomknite svoju obrazovku, aby ste ho povolili.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'Email already registered.'; - - @override - String get emailNotRegistered => 'Email not registered.'; - - @override - String get thisEmailIsAlreadyInUse => 'Tento e-mail sa už používa'; - - @override - String emailChangedTo(String newEmail) { - return 'Emailová adresa bola zmenená na $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Overenie zlyhalo. Skúste to znova'; - - @override - String get authenticationSuccessful => 'Overenie sa podarilo!'; - - @override - String get sessionExpired => 'Relácia vypršala'; - - @override - String get incorrectRecoveryKey => 'Nesprávny kľúč na obnovenie'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Kľúč na obnovenie, ktorý ste zadali, je nesprávny'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Dvojfaktorové overovanie bolo úspešne obnovené'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Platnosť overovacieho kódu uplynula'; - - @override - String get incorrectCode => 'Neplatný kód'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Ľutujeme, zadaný kód je nesprávny'; - - @override - String get developerSettings => 'Nastavenia pre vývojárov'; - - @override - String get serverEndpoint => 'Endpoint servera'; - - @override - String get invalidEndpoint => 'Neplatný endpoint'; - - @override - String get invalidEndpointMessage => - 'Ospravedlňujeme sa, endpoint, ktorý ste zadali, je neplatný. Zadajte platný endpoint a skúste to znova.'; - - @override - String get endpointUpdatedMessage => 'Endpoint úspešne aktualizovaný'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 3a2c2fc4f7..8a1a1e1dbe 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -103,528 +103,333 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Унесите Ваш нови имејл'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Имејл'; + String get email => 'Email'; @override - String get verify => 'Верификуј'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Погрешна имејл адреса'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Унесите важећи имејл.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Молимо сачекајте...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Верификујте лозинку'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Неисправна лозинка'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Пробајте поново'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Унеси лозинку'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Унесите лозинку'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Активне сесије'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Упс'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Нешто је пошло наопако. Покушајте поново'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Ово ће вас одјавити из овог уређаја!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Ово ће вас одјавити из овог уређаја:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Прекинути сесију?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Прекини'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Овај уређај'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Направи налог'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Слабо'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Умерено'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Јако'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Избриши налог'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Жао нам је што одлазите. Да ли се суочавате са неком грешком?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Да, послати повратне информације'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Не, избрисати налог'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Молимо вас да се аутентификујете за брисање рачуна'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Потврда брисања рачуна'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Овај налог је повезан са другим Ente апликацијама, ако користите било коју.\n\nВаши преношени подаци, на свим Ente апликацијама биће заказани за брисање, и ваш рачун ће се трајно избрисати.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Обриши'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Креирај нови налог'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Лозинка'; + String get password => 'Password'; @override - String get confirmPassword => 'Потврдите лозинку'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Снага лозинке: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Како сте чули о Ente? (опционо)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Не пратимо инсталацију апликације. Помогло би да нам кажеш како си нас нашао!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Прихватам услове сервиса и политику приватности'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Услови'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Политика приватности'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Разумем да ако изгубим лозинку, могу изгубити своје податке пошто су шифрирани од краја до краја.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Шифровање'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Пријави се'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Добродошли назад!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Кликом на пријаву, прихватам услове сервиса и политику приватности'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Нема интернет везе'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Провери своју везу са интернетом и покушај поново.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Неуспешна верификација, покушајте поново'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Поново креирати лозинку'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Тренутни уређај није довољно моћан да потврди вашу лозинку, али можемо регенерирати на начин који ради са свим уређајима.\n\nПријавите се помоћу кључа за опоравак и обновите своју лозинку (можете поново користити исту ако желите).'; + '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).'; @override - String get useRecoveryKey => 'Користите кључ за опоравак'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Заборавио сам лозинку'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Промени имејл'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Потврди имејл'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Послали смо имејл на $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Да бисте ресетовали лозинку, прво потврдите свој имејл.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Молимо вас да проверите примљену пошту (и нежељену пошту) да бисте довршили верификацију'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Пипните да бисте унели кôд'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Шаљи имејл'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Поново послати имејл'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Верификација је још у току'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Сесија је истекла'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Ваша сесија је истекла. Молимо пријавите се поново.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Верификација сигурносном кључем'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Чека се верификација...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Покушај поново'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Провери статус'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Пријава са TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Опоравак налога'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Постави лозинку'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Промени лозинку'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Ресетуј лозинку'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Кључеве шифровања'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Унесите лозинку за употребу за шифровање ваших података'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Унесите нову лозинку за употребу за шифровање ваших података'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Не чувамо ову лозинку, па ако је заборавите, не можемо дешифрирати ваше податке'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Како то функционише'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Генерисање кључева за шифровање...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Лозинка је успешно промењена'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Одјави се из других уређаја'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Ако мислиш да неко може знати твоју лозинку, можеш приморати одјављивање све остале уређаје које користе твој налог.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Одјави друге уређаје'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Не одјави'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Генерисање кључева за шифровање...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Настави'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Уређај није сигуран'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Извините, не можемо да генеришемо сигурне кључеве на овом уређају.\n\nМолимо пријавите се са другог уређаја.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Кључ за опоравак копирано у остави'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Резервни Кључ'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Ако заборавите лозинку, једини начин на који можете повратити податке је са овим кључем.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Не чувамо овај кључ, молимо да сачувате кључ од 24 речи на сигурном месту.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Уради то касније'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Сачувај кључ'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Кључ за опоравак сачуван у фасцикли за преузимање!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Немате кључ за опоравак?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Дво-факторска аутентификација'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Унесите 6-цифрени кôд из\nапликације за аутентификацију'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Узгубили сте уређај?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Унети кључ за опоравак'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Опорави'; - - @override - String get loggingOut => 'Одјављивање...'; - - @override - String get immediately => 'Одмах'; - - @override - String get appLock => 'Закључавање апликације'; - - @override - String get autoLock => 'Ауто-закључавање'; - - @override - String get noSystemLockFound => 'Није пронађено ниједно закључавање система'; - - @override - String get deviceLockEnablePreSteps => - 'Да бисте омогућили закључавање уређаја, молимо вас да подесите шифру уређаја или закључавање екрана у системским подешавањима.'; - - @override - String get appLockDescription => - 'Изаберите између заданог закључавање екрана вашег уређаја и прилагођени екран за закључавање са ПИН-ом или лозинком.'; - - @override - String get deviceLock => 'Закључавање уређаја'; - - @override - String get pinLock => 'ПИН клокирање'; - - @override - String get autoLockFeatureDescription => - 'Време након којег се апликација блокира након што је постављенеа у позадину'; - - @override - String get hideContent => 'Сакриј садржај'; - - @override - String get hideContentDescriptionAndroid => - 'Сакрива садржај апликације у пребацивање апликација и онемогућује снимке екрана'; - - @override - String get hideContentDescriptioniOS => - 'Сакрива садржај апликације у пребацивање апликација'; - - @override - String get tooManyIncorrectAttempts => 'Превише погрешних покушаја'; - - @override - String get tapToUnlock => 'Додирните да бисте откључали'; - - @override - String get areYouSureYouWantToLogout => 'Да ли сте сигурни да се одјавите?'; - - @override - String get yesLogout => 'Да, одјави ме'; - - @override - String get authToViewSecrets => - 'Аутентификујте се да бисте прегледали Ваше тајне'; - - @override - String get next => 'Следеће'; - - @override - String get setNewPassword => 'Постави нову лозинку'; - - @override - String get enterPin => 'Унеси ПИН'; - - @override - String get setNewPin => 'Постави нови ПИН'; - - @override - String get confirm => 'Потврди'; - - @override - String get reEnterPassword => 'Поново унеси лозинку'; - - @override - String get reEnterPin => 'Поново унеси ПИН'; - - @override - String get androidBiometricHint => 'Потврдите идентитет'; - - @override - String get androidBiometricNotRecognized => - 'Нисмо препознали. Покушати поново.'; - - @override - String get androidBiometricSuccess => 'Успех'; - - @override - String get androidCancelButton => 'Откажи'; - - @override - String get androidSignInTitle => 'Потребна аутентификација'; - - @override - String get androidBiometricRequiredTitle => 'Потребна је биометрија'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Потребни су акредитиви уређаја'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Потребни су акредитиви уређаја'; - - @override - String get goToSettings => 'Иди на поставке'; - - @override - String get androidGoToSettingsDescription => - 'Биометријска аутентификација није постављена на вашем уређају. Идите на \"Подешавања> Сигурност\" да бисте додали биометријску аутентификацију.'; - - @override - String get iOSLockOut => - 'Биометријска аутентификација је онемогућена. Закључајте и откључите екран да бисте је омогућили.'; - - @override - String get iOSOkButton => 'У реду'; - - @override - String get emailAlreadyRegistered => 'Имејл је већ регистрован.'; - - @override - String get emailNotRegistered => 'Имејл није регистрован.'; - - @override - String get thisEmailIsAlreadyInUse => 'Овај имејл је већ у употреби'; - - @override - String emailChangedTo(String newEmail) { - return 'Имејл промењен на $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Аутентификација није успела, покушајте поново'; - - @override - String get authenticationSuccessful => 'Успешна аутентификација!'; - - @override - String get sessionExpired => 'Сесија је истекла'; - - @override - String get incorrectRecoveryKey => 'Нетачан кључ за опоравак'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Унети кључ за опоравак је натачан'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Двофакторска аутентификација успешно рисетирана'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Ваш верификациони кôд је истекао'; - - @override - String get incorrectCode => 'Погрешан кôд'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => 'Унет кôд није добар'; - - @override - String get developerSettings => 'Подешавања за програмере'; - - @override - String get serverEndpoint => 'Крајња тачка сервера'; - - @override - String get invalidEndpoint => 'Погрешна крајња тачка'; - - @override - String get invalidEndpointMessage => - 'Извини, крајња тачка коју си унео је неважећа. Унеси важећу крајњу тачку и покушај поново.'; - - @override - String get endpointUpdatedMessage => 'Крајна тачка успешно ажурирана'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index 96d85243d0..f20dce0c0c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -103,526 +103,333 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get saveOnlyDescription => - 'Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Ange din nya e-postadress'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'E-post'; + String get email => 'Email'; @override - String get verify => 'Verifiera'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Ogiltig e-postadress'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Ange en giltig e-postadress.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Vänligen vänta...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Bekräfta lösenord'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Felaktigt lösenord'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Försök igen'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Ange lösenord'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Ange ditt lösenord'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktiva sessioner'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Hoppsan'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Något gick fel, vänligen försök igen'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Detta kommer att logga ut dig från den här enheten!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Detta kommer att logga ut dig från följande enhet:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Avsluta session?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Avsluta'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Den här enheten'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Skapa konto'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Svag'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Måttligt'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Stark'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Radera konto'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Vi kommer att vara ledsna över att se dig gå. Har du något problem?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Ja, skicka feedback'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Nej, radera konto'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Vänligen autentisera för att initiera borttagning av konto'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Bekräfta radering av kontot'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Radera'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Skapa nytt konto'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Lösenord'; + String get password => 'Password'; @override - String get confirmPassword => 'Bekräfta lösenord'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Lösenordsstyrka: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Hur hörde du talas om Ente? (valfritt)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Jag samtycker till användarvillkoren och integritetspolicyn'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Villkor'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Integritetspolicy'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är end-to-end-krypterad.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Kryptering'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Logga in'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Välkommen tillbaka!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Jag samtycker till användarvillkoren och integritetspolicyn'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Ingen internetanslutning'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Kontrollera din internetanslutning och försök igen.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Verifiering misslyckades, vänligen försök igen'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Återskapa lösenord'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Denna enhet är inte tillräckligt kraftfull för att verifiera ditt lösenord, men vi kan återskapa det på ett sätt som fungerar med alla enheter.\n\nLogga in med din återställningsnyckel och återskapa ditt lösenord (du kan använda samma igen om du vill).'; + '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).'; @override - String get useRecoveryKey => 'Använd återställningsnyckel'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Glömt lösenord'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Ändra e-postadress'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Verifiera e-postadress'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Vi har skickat ett mail till $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'För att återställa ditt lösenord måste du först bekräfta din e-postadress.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Tryck för att ange kod'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Skicka e-post'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Skicka e-post igen'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Verifiering pågår fortfarande'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Sessionen har gått ut'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Din session har upphört. Logga in igen.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Verifiering med inloggningsnyckel'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Väntar på verifiering...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Försök igen'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Kontrollera status'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Logga in med TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Återställ konto'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Ställ in lösenord'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Ändra lösenord'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Återställ lösenord'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Krypteringsnycklar'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Ange ett lösenord som vi kan använda för att kryptera din data'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Ange ett nytt lösenord som vi kan använda för att kryptera din data'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Vi lagrar inte detta lösenord, så om du glömmer bort det, kan vi inte dekryptera dina data'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Så här fungerar det'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Skapar krypteringsnycklar...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Lösenordet har ändrats'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Logga ut från andra enheter'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Om du tror att någon kanske känner till ditt lösenord kan du tvinga alla andra enheter med ditt konto att logga ut.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Logga ut andra enheter'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Logga inte ut'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Skapar krypteringsnycklar...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Fortsätt'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Osäker enhet'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Återställningsnyckel kopierad till urklipp'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Återställningsnyckel'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Gör detta senare'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Spara nyckel'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Återställningsnyckel sparad i nedladdningsmappen!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Ingen återställningsnyckel?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Tvåfaktorsautentisering'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Ange den 6-siffriga koden från din autentiseringsapp'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Förlorad enhet?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Ange din återställningsnyckel'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Återställ'; - - @override - String get loggingOut => 'Loggar ut...'; - - @override - String get immediately => 'Omedelbart'; - - @override - String get appLock => 'Applås'; - - @override - String get autoLock => 'Automatisk låsning'; - - @override - String get noSystemLockFound => 'Inget systemlås hittades'; - - @override - String get deviceLockEnablePreSteps => - 'För att aktivera enhetslås, vänligen ställ in enhetens lösenord eller skärmlås i dina systeminställningar.'; - - @override - String get appLockDescription => - 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; - - @override - String get deviceLock => 'Enhetslås'; - - @override - String get pinLock => 'Pinkodslås'; - - @override - String get autoLockFeatureDescription => - 'Time after which the app locks after being put in the background'; - - @override - String get hideContent => 'Dölj innehåll'; - - @override - String get hideContentDescriptionAndroid => - 'Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar'; - - @override - String get hideContentDescriptioniOS => 'Döljer appinnehåll i app-växlaren'; - - @override - String get tooManyIncorrectAttempts => 'För många felaktiga försök'; - - @override - String get tapToUnlock => 'Tryck för att låsa upp'; - - @override - String get areYouSureYouWantToLogout => - 'Är du säker på att du vill logga ut?'; - - @override - String get yesLogout => 'Ja, logga ut'; - - @override - String get authToViewSecrets => - 'Autentisera för att visa din återställningsnyckel'; - - @override - String get next => 'Nästa'; - - @override - String get setNewPassword => 'Ställ in nytt lösenord'; - - @override - String get enterPin => 'Ange PIN-kod'; - - @override - String get setNewPin => 'Ställ in ny PIN-kod'; - - @override - String get confirm => 'Bekräfta'; - - @override - String get reEnterPassword => 'Ange lösenord igen'; - - @override - String get reEnterPin => 'Ange PIN-kod igen'; - - @override - String get androidBiometricHint => 'Verifiera identitet'; - - @override - String get androidBiometricNotRecognized => 'Ej godkänd. Försök igen.'; - - @override - String get androidBiometricSuccess => 'Slutförd'; - - @override - String get androidCancelButton => 'Avbryt'; - - @override - String get androidSignInTitle => 'Obligatorisk autentisering'; - - @override - String get androidBiometricRequiredTitle => 'Biometriska uppgifter krävs'; - - @override - String get androidDeviceCredentialsRequiredTitle => 'Enhetsuppgifter krävs'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Enhetsuppgifter krävs'; - - @override - String get goToSettings => 'Gå till inställningar'; - - @override - String get androidGoToSettingsDescription => - 'Biometrisk autentisering är inte konfigurerad på din enhet. Gå till \"Inställningar > Säkerhet\" för att lägga till biometrisk autentisering.'; - - @override - String get iOSLockOut => - 'Biometrisk autentisering är inaktiverat. Lås och lås upp din skärm för att aktivera den.'; - - @override - String get iOSOkButton => 'OK'; - - @override - String get emailAlreadyRegistered => 'E-postadress redan registrerad.'; - - @override - String get emailNotRegistered => 'E-postadress ej registrerad.'; - - @override - String get thisEmailIsAlreadyInUse => 'Denna e-postadress används redan'; - - @override - String emailChangedTo(String newEmail) { - return 'E-post ändrad till $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Autentisering misslyckades, vänligen försök igen'; - - @override - String get authenticationSuccessful => 'Autentisering lyckades!'; - - @override - String get sessionExpired => 'Sessionen har gått ut'; - - @override - String get incorrectRecoveryKey => 'Felaktig återställningsnyckel'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Återställningsnyckeln du angav är felaktig'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Tvåfaktorsautentisering återställd'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Din verifieringskod har upphört att gälla'; - - @override - String get incorrectCode => 'Felaktig kod'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Tyvärr, den kod som du har angett är felaktig'; - - @override - String get developerSettings => 'Utvecklarinställningar'; - - @override - String get serverEndpoint => 'Serverns slutpunkt'; - - @override - String get invalidEndpoint => 'Ogiltig slutpunkt'; - - @override - String get invalidEndpointMessage => - 'Tyvärr, slutpunkten du angav är ogiltig. Ange en giltig slutpunkt och försök igen.'; - - @override - String get endpointUpdatedMessage => 'Slutpunkten har uppdaterats'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index c5be60b8db..c1b02a9f9f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -103,530 +103,333 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Bunu belleğinize kaydetmek ister misiniz? (İndirilenler klasörü varsayılandır)'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override - String get enterNewEmailHint => 'Yeni e-posta adresinizi girin'; + String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'E-Posta'; + String get email => 'Email'; @override - String get verify => 'Doğrula'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Geçersiz e-posta adresi'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => 'Lütfen geçerli bir e-posta adresi girin.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Lütfen bekleyin...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Şifreyi doğrulayın'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Yanlış şifre'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Lütfen tekrar deneyin'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Şifreyi girin'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Parolanızı girin'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Aktif oturumlar'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Hay aksi'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Bir şeyler ters gitti, lütfen tekrar deneyin'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Bu sizin bu cihazdaki oturumunuzu kapatacaktır!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Bu, aşağıdaki cihazdan çıkış yapmanızı sağlayacaktır:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Oturumu sonlandır?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Sonlandır'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Bu cihaz'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Hesap oluştur'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Zayıf'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Orta'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Güçlü'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Hesabı sil'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Sizin gittiğinizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Evet, geri bildirimi gönder'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Hayır, hesabı sil'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Hesap silme işlemini yapabilmek için lütfen kimliğinizi doğrulayın'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Hesap silme işlemini onayla'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Kullandığınız Ente uygulamaları varsa bu hesap diğer Ente uygulamalarıyla bağlantılıdır.\n\nTüm Ente uygulamalarına yüklediğiniz veriler ve hesabınız kalıcı olarak silinecektir.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Sil'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Yeni hesap oluşturun'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Şifre'; + String get password => 'Password'; @override - String get confirmPassword => 'Şifreyi onayla'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Şifre gücü: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'Ente\'yi nereden duydunuz? (opsiyonel)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Biz uygulama kurulumlarını takip etmiyoruz. Bizi nereden duyduğunuzdan bahsetmeniz bize çok yardımcı olacak!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Kullanım şartlarını ve gizlilik politikasını kabul ediyorum'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Şartlar'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Gizlilik Politikası'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Eğer şifremi kaybedersem, verilerim uçtan uca şifrelendiğinden verilerimi kaybedebileceğimi anladım.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Şifreleme'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Giriş yapın'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Tekrar hoş geldiniz!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Giriş yaparak, kullanım şartları nı ve gizlilik politikası nı onaylıyorum'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'İnternet bağlantısı yok'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Lütfen internet bağlantınızı kontrol edin ve yeniden deneyin.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Doğrulama başarısız oldu, lütfen tekrar deneyin'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Şifreyi yeniden oluştur'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Mevcut cihaz şifrenizi doğrulayacak kadar güçlü değil, ancak tüm cihazlarla çalışacak şekilde yeniden oluşturabiliriz.\n\nLütfen kurtarma anahtarınızı kullanarak giriş yapın ve şifrenizi yeniden oluşturun (isterseniz aynı şifreyi tekrar kullanabilirsiniz).'; + '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).'; @override - String get useRecoveryKey => 'Kurtarma anahtarını kullan'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Şifremi unuttum'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'E-posta adresini değiştir'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'E-posta adresini doğrulayın'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return '$email adresine bir posta gönderdik'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Şifrenizi sıfırlamak için lütfen önce e-postanızı doğrulayın.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Doğrulamayı tamamlamak için lütfen gelen kutunuzu (ve spam kutunuzu) kontrol edin'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Kodu girmek için dokunun'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'E-posta gönder'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'E-postayı yeniden gönder'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Doğrulama hala bekliyor'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Oturum süresi doldu'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Oturum süreniz doldu. Tekrar giriş yapın.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Geçiş anahtarı doğrulaması'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Doğrulama bekleniyor...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Tekrar deneyin'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Durumu kontrol et'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'TOTP ile giriş yap'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Hesap kurtarma'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Şifre belirleyin'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Şifreyi değiştirin'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Şifreyi sıfırlayın'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Şifreleme anahtarları'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Verilerinizi şifrelemek için kullanabileceğimiz bir şifre girin'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Verilerinizi şifrelemek için kullanabileceğimiz yeni bir şifre girin'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Bu şifreyi saklamıyoruz, bu nedenle unutursanız, verilerinizin şifresini çözemeyiz'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Nasıl çalışır'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => - 'Şifreleme anahtarları oluşturuluyor...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Şifre başarıyla değiştirildi'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Diğer cihazlardan çıkış yap'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Eğer başka birisinin parolanızı bildiğini düşünüyorsanız, diğer tüm cihazları hesabınızdan çıkışa zorlayabilirsiniz.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Diğer cihazlardan çıkış yap'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Çıkış yapma'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => - 'Şifreleme anahtarları üretiliyor...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Devam et'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Güvenli olmayan cihaz'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Üzgünüz, bu cihazda güvenli anahtarlar oluşturamadık.\n\nlütfen farklı bir cihazdan kaydolun.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Kurtarma anahtarı panoya kopyalandı'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Kurtarma Anahtarı'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Eğer şifrenizi unutursanız, verilerinizi kurtarabileceğiniz tek yol bu anahtardır.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Biz bu anahtarı saklamıyoruz, lütfen. bu 24 kelimelik anahtarı güvenli bir yerde saklayın.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Bunu daha sonra yap'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Anahtarı kaydet'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => - 'Kurtarma anahtarı İndirilenler klasörüne kaydedildi!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Kurtarma anahtarınız yok mu?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'İki faktörlü kimlik doğrulama'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Kimlik doğrulayıcı uygulamanızdaki 6 haneli doğrulama kodunu girin'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Cihazınızı mı kaybettiniz?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Kurtarma anahtarınızı girin'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Kurtar'; - - @override - String get loggingOut => 'Çıkış yapılıyor...'; - - @override - String get immediately => 'Hemen'; - - @override - String get appLock => 'Uygulama kilidi'; - - @override - String get autoLock => 'Otomatik Kilit'; - - @override - String get noSystemLockFound => 'Sistem kilidi bulunamadı'; - - @override - String get deviceLockEnablePreSteps => - 'Cihaz kilidini etkinleştirmek için, lütfen cihaz şifresini veya ekran kilidini ayarlayın.'; - - @override - String get appLockDescription => - 'Cihazınızın varsayılan kilit ekranı ile PIN veya parola içeren özel bir kilit ekranı arasında seçim yapın.'; - - @override - String get deviceLock => 'Cihaz kilidi'; - - @override - String get pinLock => 'Pin kilidi'; - - @override - String get autoLockFeatureDescription => - 'Uygulamayı arka plana attıktan sonra kilitlendiği süre'; - - @override - String get hideContent => 'İçeriği gizle'; - - @override - String get hideContentDescriptionAndroid => - 'Uygulama değiştiricide bulunan uygulama içeriğini gizler ve ekran görüntülerini devre dışı bırakır'; - - @override - String get hideContentDescriptioniOS => - 'Uygulama değiştiricideki uygulama içeriğini gizler'; - - @override - String get tooManyIncorrectAttempts => 'Çok fazla hatalı deneme'; - - @override - String get tapToUnlock => 'Açmak için dokun'; - - @override - String get areYouSureYouWantToLogout => - 'Çıkış yapmak istediğinize emin misiniz?'; - - @override - String get yesLogout => 'Evet, çıkış yap'; - - @override - String get authToViewSecrets => - 'Kodlarınızı görmek için lütfen kimlik doğrulaması yapın'; - - @override - String get next => 'Sonraki'; - - @override - String get setNewPassword => 'Yeni şifre belirle'; - - @override - String get enterPin => 'PIN Girin'; - - @override - String get setNewPin => 'Yeni PIN belirleyin'; - - @override - String get confirm => 'Doğrula'; - - @override - String get reEnterPassword => 'Şifrenizi tekrar girin'; - - @override - String get reEnterPin => 'PIN\'inizi tekrar girin'; - - @override - String get androidBiometricHint => 'Kimliği doğrula'; - - @override - String get androidBiometricNotRecognized => 'Tanınmadı. Tekrar deneyin.'; - - @override - String get androidBiometricSuccess => 'Başarılı'; - - @override - String get androidCancelButton => 'İptal et'; - - @override - String get androidSignInTitle => 'Kimlik doğrulaması gerekli'; - - @override - String get androidBiometricRequiredTitle => 'Biyometrik gerekli'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Cihaz kimlik bilgileri gerekli'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Cihaz kimlik bilgileri gerekmekte'; - - @override - String get goToSettings => 'Ayarlara git'; - - @override - String get androidGoToSettingsDescription => - 'Biyometrik kimlik doğrulama cihazınızda ayarlanmamış. Biyometrik kimlik doğrulama eklemek için \'Ayarlar > Güvenlik\' bölümüne gidin.'; - - @override - String get iOSLockOut => - 'Biyometrik kimlik doğrulama devre dışı. Etkinleştirmek için lütfen ekranınızı kilitleyin ve kilidini açın.'; - - @override - String get iOSOkButton => 'Tamam'; - - @override - String get emailAlreadyRegistered => 'E-posta zaten kayıtlı.'; - - @override - String get emailNotRegistered => 'E-posta kayıtlı değil.'; - - @override - String get thisEmailIsAlreadyInUse => 'Bu e-posta zaten kullanılıyor'; - - @override - String emailChangedTo(String newEmail) { - return 'E-posta $newEmail olarak değiştirildi'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Kimlik doğrulama başarısız oldu, lütfen tekrar deneyin'; - - @override - String get authenticationSuccessful => 'Kimlik doğrulama başarılı!'; - - @override - String get sessionExpired => 'Oturum süresi doldu'; - - @override - String get incorrectRecoveryKey => 'Yanlış kurtarma kodu'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Girdiğiniz kurtarma kodu yanlış'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'İki faktörlü kimlik doğrulama başarıyla sıfırlandı'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Doğrulama kodunuzun süresi doldu'; - - @override - String get incorrectCode => 'Yanlış kod'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Üzgünüz, girdiğiniz kod yanlış'; - - @override - String get developerSettings => 'Geliştirici ayarları'; - - @override - String get serverEndpoint => 'Sunucu uç noktası'; - - @override - String get invalidEndpoint => 'Geçersiz uç nokta'; - - @override - String get invalidEndpointMessage => - 'Üzgünüz, girdiğiniz uç nokta geçersiz. Lütfen geçerli bir uç nokta girin ve tekrar deneyin.'; - - @override - String get endpointUpdatedMessage => 'Uç nokta başarıyla güncellendi'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index a55e1a242c..d231567f04 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -103,526 +103,333 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get saveOnlyDescription => - 'Bạn có muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?'; + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Thư điện tử'; + String get email => 'Email'; @override - String get verify => 'Xác minh'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => 'Địa chỉ email không hợp lệ'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => - 'Xin vui lòng nhập một địa chỉ email hợp lệ.'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => 'Vui lòng chờ...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => 'Xác nhận mật khẩu'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => 'Mật khẩu không đúng'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => 'Vui lòng thử lại'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => 'Nhập mật khẩu'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => 'Nhập mật khẩu của bạn'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => 'Các phiên làm việc hiện tại'; + String get activeSessions => 'Active sessions'; @override - String get oops => 'Rất tiếc'; + String get oops => 'Oops'; @override String get somethingWentWrongPleaseTryAgain => - 'Phát hiện có lỗi, xin thử lại'; + 'Something went wrong, please try again'; @override String get thisWillLogYouOutOfThisDevice => - 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị này!'; + 'This will log you out of this device!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị sau:'; + 'This will log you out of the following device:'; @override - String get terminateSession => 'Kết thúc phiên?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => 'Kết thúc'; + String get terminate => 'Terminate'; @override - String get thisDevice => 'Thiết bị này'; + String get thisDevice => 'This device'; @override - String get createAccount => 'Tạo tài khoản'; + String get createAccount => 'Create account'; @override - String get weakStrength => 'Yếu'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => 'Trung bình'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => 'Mạnh'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => 'Xoá tài khoản'; + String get deleteAccount => 'Delete account'; @override String get deleteAccountQuery => - 'Chúng tôi sẽ rất tiếc khi thấy bạn đi. Bạn đang phải đối mặt với một số vấn đề?'; + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => 'Có, gửi phản hồi'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => 'Không, xóa tài khoản'; + String get noDeleteAccountAction => 'No, delete account'; @override String get initiateAccountDeleteTitle => - 'Vui lòng xác thực để bắt đầu xóa tài khoản'; + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => 'Xác nhận xóa tài khoản'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - 'Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => 'Xóa'; + String get delete => 'Delete'; @override - String get createNewAccount => 'Tạo tài khoản mới'; + String get createNewAccount => 'Create new account'; @override - String get password => 'Mật khẩu'; + String get password => 'Password'; @override - String get confirmPassword => 'Xác nhận mật khẩu'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return 'Độ mạnh mật khẩu: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => - 'Bạn biết đến Ente bằng cách nào? (không bắt buộc)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override String get hearUsExplanation => - 'Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!'; + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - 'Tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => 'Điều khoản'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => 'Chính sách bảo mật'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - 'Tôi hiểu rằng việc mất mật khẩu có thể đồng nghĩa với việc mất dữ liệu của tôi vì dữ liệu của tôi được mã hóa hai đầu.'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => 'Mã hóa'; + String get encryption => 'Encryption'; @override - String get logInLabel => 'Đăng nhập'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => 'Chào mừng trở lại!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - 'Bằng cách nhấp vào đăng nhập, tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => 'Không có kết nối Internet'; + String get noInternetConnection => 'No internet connection'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Vui lòng kiểm tra kết nối internet của bạn và thử lại.'; + 'Please check your internet connection and try again.'; @override String get verificationFailedPleaseTryAgain => - 'Mã xác nhận thất bại. Vui lòng thử lại'; + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => 'Tạo lại mật khẩu'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - 'Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn nhưng chúng tôi có thể tạo lại mật khẩu theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dụng lại cùng một mật khẩu nếu muốn).'; + '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).'; @override - String get useRecoveryKey => 'Dùng khóa khôi phục'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => 'Quên mật khẩu'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => 'Thay đổi email'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => 'Xác nhận địa chỉ Email'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return 'Chúng tôi đã gửi thư đến $email'; + return 'We have sent a mail to $email'; } @override String get toResetVerifyEmail => - 'Để đặt lại mật khẩu, vui lòng xác minh email của bạn trước.'; + 'To reset your password, please verify your email first.'; @override String get checkInboxAndSpamFolder => - 'Vui lòng kiểm tra hộp thư đến (và thư rác) của bạn để hoàn tất xác minh'; + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => 'Chạm để nhập mã'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => 'Gửi email'; + String get sendEmail => 'Send email'; @override - String get resendEmail => 'Gửi lại email'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => 'Đang chờ xác thực'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => 'Phiên làm việc hết hạn'; + String get loginSessionExpired => 'Session expired'; @override String get loginSessionExpiredDetails => - 'Phiên làm việc hết hạn. Vui lòng đăng nhập lại.'; + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => 'Xác minh mã khóa'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Đang chờ xác thực'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => 'Thử lại'; + String get tryAgain => 'Try again'; @override - String get checkStatus => 'Kiểm tra trạng thái'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => 'Đăng nhập bằng TOTP'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Khôi phục tài khoản'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => 'Đặt mật khẩu'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => 'Thay đổi mật khẩu'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => 'Đặt lại mật khẩu'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => 'Khóa mã hóa'; + String get encryptionKeys => 'Encryption keys'; @override String get enterPasswordToEncrypt => - 'Nhập mật khẩu mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; + 'Enter a password we can use to encrypt your data'; @override String get enterNewPasswordToEncrypt => - 'Nhập một mật khẩu mới mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - 'Chúng tôi không lưu trữ mật khẩu này, vì vậy nếu bạn quên, chúng tôi không thể giải mã dữ liệu của bạn'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => 'Cách thức hoạt động'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => 'Đang tạo khóa mã hóa...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => 'Thay đổi mật khẩu thành công'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override String get signOutOtherBody => - 'Nếu bạn cho rằng ai đó có thể biết mật khẩu của mình, bạn có thể buộc đăng xuất tất cả các thiết bị khác đang sử dụng tài khoản của mình.'; + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => 'Không được đăng xuất'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => 'Đang tạo khóa mã hóa...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => 'Tiếp tục'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => 'Thiết bị không an toàn'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Rất tiếc, chúng tôi không thể tạo khóa bảo mật trên thiết bị này.\n\nvui lòng đăng ký từ một thiết bị khác.'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => - 'Đã sao chép khóa khôi phục vào bộ nhớ tạm'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => 'Khóa khôi phục'; + String get recoveryKey => 'Recovery key'; @override String get recoveryKeyOnForgotPassword => - 'Nếu bạn quên mật khẩu, cách duy nhất bạn có thể khôi phục dữ liệu của mình là sử dụng khóa này.'; + 'If you forget your password, the only way you can recover your data is with this key.'; @override String get recoveryKeySaveDescription => - 'Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.'; + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => 'Để sau'; + String get doThisLater => 'Do this later'; @override - String get saveKey => 'Lưu khóa'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => 'Đã lưu khoá dự phòng vào thư mục Tải về!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => 'Không có khóa khôi phục?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => 'Xác thực hai yếu tố'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override String get enterCodeHint => - 'Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn'; + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => 'Mất thiết bị?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => 'Nhập khóa khôi phục của bạn'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => 'Khôi phục'; - - @override - String get loggingOut => 'Đang đăng xuất...'; - - @override - String get immediately => 'Tức thì'; - - @override - String get appLock => 'Khóa ứng dụng'; - - @override - String get autoLock => 'Tự động khóa'; - - @override - String get noSystemLockFound => 'Không thấy khoá hệ thống'; - - @override - String get deviceLockEnablePreSteps => - 'Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.'; - - @override - String get appLockDescription => - 'Chọn giữa màn hình khoá mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.'; - - @override - String get deviceLock => 'Khóa thiết bị'; - - @override - String get pinLock => 'Mã PIN'; - - @override - String get autoLockFeatureDescription => - 'Thời gian ứng dụng tự khoá sau khi ở trạng thái nền'; - - @override - String get hideContent => 'Ẩn nội dung'; - - @override - String get hideContentDescriptionAndroid => - 'Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình'; - - @override - String get hideContentDescriptioniOS => 'Ẩn nội dung khi chuyển ứng dụng'; - - @override - String get tooManyIncorrectAttempts => 'Quá nhiều lần thử không chính xác'; - - @override - String get tapToUnlock => 'Nhấn để mở khóa'; - - @override - String get areYouSureYouWantToLogout => 'Bạn có chắc chắn muốn đăng xuất?'; - - @override - String get yesLogout => 'Có, đăng xuất'; - - @override - String get authToViewSecrets => 'Vui lòng xác thực để xem bí mật của bạn'; - - @override - String get next => 'Tiếp theo'; - - @override - String get setNewPassword => 'Đặt lại mật khẩu'; - - @override - String get enterPin => 'Nhập mã PIN'; - - @override - String get setNewPin => 'Đổi mã PIN'; - - @override - String get confirm => 'Xác nhận'; - - @override - String get reEnterPassword => 'Nhập lại mật khẩu'; - - @override - String get reEnterPin => 'Nhập lại mã PIN'; - - @override - String get androidBiometricHint => 'Xác định danh tính'; - - @override - String get androidBiometricNotRecognized => - 'Không nhận dạng được. Vui lòng thử lại.'; - - @override - String get androidBiometricSuccess => 'Thành công'; - - @override - String get androidCancelButton => 'Hủy bỏ'; - - @override - String get androidSignInTitle => 'Yêu cầu xác thực'; - - @override - String get androidBiometricRequiredTitle => 'Yêu cầu sinh trắc học'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Yêu cầu thông tin xác thực thiết bị'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Yêu cầu thông tin xác thực thiết bị'; - - @override - String get goToSettings => 'Chuyển đến cài đặt'; - - @override - String get androidGoToSettingsDescription => - 'Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi tới \'Cài đặt > Bảo mật\' để thêm xác thực sinh trắc học.'; - - @override - String get iOSLockOut => - 'Xác thực sinh trắc học bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt nó.'; - - @override - String get iOSOkButton => 'Đồng ý'; - - @override - String get emailAlreadyRegistered => 'Email đã được đăng kí.'; - - @override - String get emailNotRegistered => 'Email chưa được đăng kí.'; - - @override - String get thisEmailIsAlreadyInUse => 'Email này đã được sử dụng'; - - @override - String emailChangedTo(String newEmail) { - return 'Thay đổi email thành $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'Xác thực lỗi, vui lòng thử lại'; - - @override - String get authenticationSuccessful => 'Xác thực thành công!'; - - @override - String get sessionExpired => 'Phiên làm việc đã hết hạn'; - - @override - String get incorrectRecoveryKey => 'Khóa khôi phục không chính xác'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'Khóa khôi phục bạn đã nhập không chính xác'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'Xác thực hai bước được khôi phục thành công'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => 'Mã xác minh của bạn đã hết hạn'; - - @override - String get incorrectCode => 'Mã không chính xác'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Xin lỗi, mã bạn đã nhập không chính xác'; - - @override - String get developerSettings => 'Cài đặt cho nhà phát triển'; - - @override - String get serverEndpoint => 'Điểm cuối máy chủ'; - - @override - String get invalidEndpoint => 'Điểm cuối không hợp lệ'; - - @override - String get invalidEndpointMessage => - 'Xin lỗi, điểm cuối bạn nhập không hợp lệ. Vui lòng nhập một điểm cuối hợp lệ và thử lại.'; - - @override - String get endpointUpdatedMessage => 'Cập nhật điểm cuối thành công'; + String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index d0447c1877..6f476f999b 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -78,6 +78,18 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get reportABug => '报告错误'; + @override + String get logsDirectoryName => 'logs'; + + @override + String get logsZipFileName => 'logs.zip'; + + @override + String get zipFileExtension => 'zip'; + + @override + String get reportABug => 'Report a bug'; + @override String get logsDialogBody => '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.'; @@ -87,7 +99,7 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return '已连接至 $endpoint'; + return 'Connected to $endpoint'; } @override @@ -101,1040 +113,336 @@ class StringsLocalizationsZh extends StringsLocalizations { '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; @override - String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; + String get saveOnlyDescription => + 'Do you want to save this to your storage (Downloads folder by default)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => '电子邮件地址'; + String get email => 'Email'; @override - String get verify => '验证'; + String get verify => 'Verify'; @override - String get invalidEmailTitle => '无效的电子邮件地址'; + String get invalidEmailTitle => 'Invalid email address'; @override - String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; + String get invalidEmailMessage => 'Please enter a valid email address.'; @override - String get pleaseWait => '请稍候...'; + String get pleaseWait => 'Please wait...'; @override - String get verifyPassword => '验证密码'; + String get verifyPassword => 'Verify password'; @override - String get incorrectPasswordTitle => '密码错误'; + String get incorrectPasswordTitle => 'Incorrect password'; @override - String get pleaseTryAgain => '请重试'; + String get pleaseTryAgain => 'Please try again'; @override - String get enterPassword => '输入密码'; + String get enterPassword => 'Enter password'; @override - String get enterYourPasswordHint => '输入您的密码'; + String get enterYourPasswordHint => 'Enter your password'; @override - String get activeSessions => '已登录的设备'; + String get activeSessions => 'Active sessions'; @override - String get oops => '哎呀'; + String get oops => 'Oops'; @override - String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; + String get somethingWentWrongPleaseTryAgain => + 'Something went wrong, please try again'; @override - String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; + String get thisWillLogYouOutOfThisDevice => + 'This will log you out of this device!'; @override - String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; + String get thisWillLogYouOutOfTheFollowingDevice => + 'This will log you out of the following device:'; @override - String get terminateSession => '是否终止会话?'; + String get terminateSession => 'Terminate session?'; @override - String get terminate => '终止'; + String get terminate => 'Terminate'; @override - String get thisDevice => '此设备'; + String get thisDevice => 'This device'; @override - String get createAccount => '创建账户'; + String get createAccount => 'Create account'; @override - String get weakStrength => '弱'; + String get weakStrength => 'Weak'; @override - String get moderateStrength => '中'; + String get moderateStrength => 'Moderate'; @override - String get strongStrength => '强'; + String get strongStrength => 'Strong'; @override - String get deleteAccount => '删除账户'; + String get deleteAccount => 'Delete account'; @override - String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; + String get deleteAccountQuery => + 'We\'ll be sorry to see you go. Are you facing some issue?'; @override - String get yesSendFeedbackAction => '是,发送反馈'; + String get yesSendFeedbackAction => 'Yes, send feedback'; @override - String get noDeleteAccountAction => '否,删除账户'; + String get noDeleteAccountAction => 'No, delete account'; @override - String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; + String get initiateAccountDeleteTitle => + 'Please authenticate to initiate account deletion'; @override - String get confirmAccountDeleteTitle => '确认删除账户'; + String get confirmAccountDeleteTitle => 'Confirm account deletion'; @override String get confirmAccountDeleteMessage => - '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; + 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; @override - String get delete => '删除'; + String get delete => 'Delete'; @override - String get createNewAccount => '创建新账号'; + String get createNewAccount => 'Create new account'; @override - String get password => '密码'; + String get password => 'Password'; @override - String get confirmPassword => '请确认密码'; + String get confirmPassword => 'Confirm password'; @override String passwordStrength(String passwordStrengthValue) { - return '密码强度: $passwordStrengthValue'; + return 'Password strength: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; + String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; @override - String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; + String get hearUsExplanation => + 'We don\'t track app installs. It\'d help if you told us where you found us!'; @override String get signUpTerms => - '我同意 服务条款隐私政策'; + 'I agree to the terms of service and privacy policy'; @override - String get termsOfServicesTitle => '服务条款'; + String get termsOfServicesTitle => 'Terms'; @override - String get privacyPolicyTitle => '隐私政策'; + String get privacyPolicyTitle => 'Privacy Policy'; @override String get ackPasswordLostWarning => - '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; + 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; @override - String get encryption => '加密'; + String get encryption => 'Encryption'; @override - String get logInLabel => '登录'; + String get logInLabel => 'Log in'; @override - String get welcomeBack => '欢迎回来!'; + String get welcomeBack => 'Welcome back!'; @override String get loginTerms => - '点击登录后,我同意 服务条款隐私政策'; + 'By clicking log in, I agree to the terms of service and privacy policy'; @override - String get noInternetConnection => '无互联网连接'; + String get noInternetConnection => 'No internet connection'; @override - String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; + String get pleaseCheckYourInternetConnectionAndTryAgain => + 'Please check your internet connection and try again.'; @override - String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; + String get verificationFailedPleaseTryAgain => + 'Verification failed, please try again'; @override - String get recreatePasswordTitle => '重新创建密码'; + String get recreatePasswordTitle => 'Recreate password'; @override String get recreatePasswordBody => - '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; + '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).'; @override - String get useRecoveryKey => '使用恢复密钥'; + String get useRecoveryKey => 'Use recovery key'; @override - String get forgotPassword => '忘记密码'; + String get forgotPassword => 'Forgot password'; @override - String get changeEmail => '修改邮箱'; + String get changeEmail => 'Change email'; @override - String get verifyEmail => '验证电子邮件'; + String get verifyEmail => 'Verify email'; @override String weHaveSendEmailTo(String email) { - return '我们已经发送邮件到 $email'; + return 'We have sent a mail to $email'; } @override - String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; + String get toResetVerifyEmail => + 'To reset your password, please verify your email first.'; @override - String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; + String get checkInboxAndSpamFolder => + 'Please check your inbox (and spam) to complete verification'; @override - String get tapToEnterCode => '点击以输入代码'; + String get tapToEnterCode => 'Tap to enter code'; @override - String get sendEmail => '发送电子邮件'; + String get sendEmail => 'Send email'; @override - String get resendEmail => '重新发送电子邮件'; + String get resendEmail => 'Resend email'; @override - String get passKeyPendingVerification => '仍需验证'; + String get passKeyPendingVerification => 'Verification is still pending'; @override - String get loginSessionExpired => '会话已过期'; + String get loginSessionExpired => 'Session expired'; @override - String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; + String get loginSessionExpiredDetails => + 'Your session has expired. Please login again.'; @override - String get passkeyAuthTitle => '通行密钥验证'; + String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => '等待验证...'; + String get waitingForVerification => 'Waiting for verification...'; @override - String get tryAgain => '请再试一次'; + String get tryAgain => 'Try again'; @override - String get checkStatus => '检查状态'; + String get checkStatus => 'Check status'; @override - String get loginWithTOTP => '使用 TOTP 登录'; + String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => '恢复账户'; + String get recoverAccount => 'Recover account'; @override - String get setPasswordTitle => '设置密码'; + String get setPasswordTitle => 'Set password'; @override - String get changePasswordTitle => '修改密码'; + String get changePasswordTitle => 'Change password'; @override - String get resetPasswordTitle => '重置密码'; + String get resetPasswordTitle => 'Reset password'; @override - String get encryptionKeys => '加密密钥'; + String get encryptionKeys => 'Encryption keys'; @override - String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; + String get enterPasswordToEncrypt => + 'Enter a password we can use to encrypt your data'; @override - String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; + String get enterNewPasswordToEncrypt => + 'Enter a new password we can use to encrypt your data'; @override String get passwordWarning => - '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; + 'We don\'t store this password, so if you forget, we cannot decrypt your data'; @override - String get howItWorks => '工作原理'; + String get howItWorks => 'How it works'; @override - String get generatingEncryptionKeys => '正在生成加密密钥...'; + String get generatingEncryptionKeys => 'Generating encryption keys...'; @override - String get passwordChangedSuccessfully => '密码修改成功'; + String get passwordChangedSuccessfully => 'Password changed successfully'; @override - String get signOutFromOtherDevices => '从其他设备登出'; + String get signOutFromOtherDevices => 'Sign out from other devices'; @override - String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; + String get signOutOtherBody => + 'If you think someone might know your password, you can force all other devices using your account to sign out.'; @override - String get signOutOtherDevices => '登出其他设备'; + String get signOutOtherDevices => 'Sign out other devices'; @override - String get doNotSignOut => '不要登出'; + String get doNotSignOut => 'Do not sign out'; @override - String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; + String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; @override - String get continueLabel => '继续'; + String get continueLabel => 'Continue'; @override - String get insecureDevice => '设备不安全'; + String get insecureDevice => 'Insecure device'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; + 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; @override - String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; + String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; @override - String get recoveryKey => '恢复密钥'; + String get recoveryKey => 'Recovery key'; @override - String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; + String get recoveryKeyOnForgotPassword => + 'If you forget your password, the only way you can recover your data is with this key.'; @override - String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; + String get recoveryKeySaveDescription => + 'We don\'t store this key, please save this 24 word key in a safe place.'; @override - String get doThisLater => '稍后再做'; + String get doThisLater => 'Do this later'; @override - String get saveKey => '保存密钥'; + String get saveKey => 'Save key'; @override - String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; + String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; @override - String get noRecoveryKeyTitle => '没有恢复密钥吗?'; + String get noRecoveryKeyTitle => 'No recovery key?'; @override - String get twoFactorAuthTitle => '两步验证'; + String get twoFactorAuthTitle => 'Two-factor authentication'; @override - String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; + String get enterCodeHint => + 'Enter the 6-digit code from\nyour authenticator app'; @override - String get lostDeviceTitle => '丢失了设备吗?'; + String get lostDeviceTitle => 'Lost device?'; @override - String get enterRecoveryKeyHint => '输入您的恢复密钥'; + String get enterRecoveryKeyHint => 'Enter your recovery key'; @override - String get recover => '恢复'; - - @override - String get loggingOut => '正在登出...'; - - @override - String get immediately => '立即'; - - @override - String get appLock => '应用锁'; - - @override - String get autoLock => '自动锁定'; - - @override - String get noSystemLockFound => '未找到系统锁'; - - @override - String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; - - @override - String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; - - @override - String get deviceLock => '设备锁'; - - @override - String get pinLock => 'Pin 锁定'; - - @override - String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; - - @override - String get hideContent => '隐藏内容'; - - @override - String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; - - @override - String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; - - @override - String get tooManyIncorrectAttempts => '错误的尝试次数过多'; - - @override - String get tapToUnlock => '点击解锁'; - - @override - String get areYouSureYouWantToLogout => '您确定要登出吗?'; - - @override - String get yesLogout => '是的,登出'; - - @override - String get authToViewSecrets => '请进行身份验证以查看您的密钥'; - - @override - String get next => '下一步'; - - @override - String get setNewPassword => '设置新密码'; - - @override - String get enterPin => '输入 PIN 码'; - - @override - String get setNewPin => '设置新 PIN 码'; - - @override - String get confirm => '确认'; - - @override - String get reEnterPassword => '再次输入密码'; - - @override - String get reEnterPin => '再次输入 PIN 码'; - - @override - String get androidBiometricHint => '验证身份'; - - @override - String get androidBiometricNotRecognized => '未能识别,请重试。'; - - @override - String get androidBiometricSuccess => '成功'; - - @override - String get androidCancelButton => '取消'; - - @override - String get androidSignInTitle => '需要进行身份验证'; - - @override - String get androidBiometricRequiredTitle => '需要进行生物识别认证'; - - @override - String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; - - @override - String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; - - @override - String get goToSettings => '前往设置'; - - @override - String get androidGoToSettingsDescription => - '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; - - @override - String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; - - @override - String get iOSOkButton => '好'; - - @override - String get emailAlreadyRegistered => '电子邮件地址已被注册。'; - - @override - String get emailNotRegistered => '电子邮件地址未注册。'; - - @override - String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; - - @override - String emailChangedTo(String newEmail) { - return '电子邮件已更改为 $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; - - @override - String get authenticationSuccessful => '认证成功!'; - - @override - String get sessionExpired => '会话已过期'; - - @override - String get incorrectRecoveryKey => '恢复密钥不正确'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; - - @override - String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => '您的验证码已过期'; - - @override - String get incorrectCode => '验证码错误'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; - - @override - String get developerSettings => '开发者设置'; - - @override - String get serverEndpoint => '服务器端点'; - - @override - String get invalidEndpoint => '端点无效'; - - @override - String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; - - @override - String get endpointUpdatedMessage => '端点更新成功'; -} - -/// The translations for Chinese, as used in China (`zh_CN`). -class StringsLocalizationsZhCn extends StringsLocalizationsZh { - StringsLocalizationsZhCn() : super('zh_CN'); - - @override - String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。'; - - @override - String get networkConnectionRefusedErr => - '无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。'; - - @override - String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => - '看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。'; - - @override - String get error => '错误'; - - @override - String get ok => '确定'; - - @override - String get faq => '常见问题'; - - @override - String get contactSupport => '联系支持'; - - @override - String get emailYourLogs => '通过电子邮件发送您的日志'; - - @override - String pleaseSendTheLogsTo(String toEmail) { - return '请将日志发送至 \n$toEmail'; - } - - @override - String get copyEmailAddress => '复制电子邮件地址'; - - @override - String get exportLogs => '导出日志'; - - @override - String get cancel => '取消'; - - @override - String get reportABug => '报告错误'; - - @override - String customEndpoint(String endpoint) { - return '已连接至 $endpoint'; - } - - @override - String get save => '保存'; - - @override - String get send => '发送'; - - @override - String get saveOrSendDescription => - '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; - - @override - String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; - - @override - String get enterNewEmailHint => '请输入您的新电子邮件地址'; - - @override - String get email => '电子邮件地址'; - - @override - String get verify => '验证'; - - @override - String get invalidEmailTitle => '无效的电子邮件地址'; - - @override - String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; - - @override - String get pleaseWait => '请稍候...'; - - @override - String get verifyPassword => '验证密码'; - - @override - String get incorrectPasswordTitle => '密码错误'; - - @override - String get pleaseTryAgain => '请重试'; - - @override - String get enterPassword => '输入密码'; - - @override - String get enterYourPasswordHint => '输入您的密码'; - - @override - String get activeSessions => '已登录的设备'; - - @override - String get oops => '哎呀'; - - @override - String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; - - @override - String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; - - @override - String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; - - @override - String get terminateSession => '是否终止会话?'; - - @override - String get terminate => '终止'; - - @override - String get thisDevice => '此设备'; - - @override - String get createAccount => '创建账户'; - - @override - String get weakStrength => '弱'; - - @override - String get moderateStrength => '中'; - - @override - String get strongStrength => '强'; - - @override - String get deleteAccount => '删除账户'; - - @override - String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; - - @override - String get yesSendFeedbackAction => '是,发送反馈'; - - @override - String get noDeleteAccountAction => '否,删除账户'; - - @override - String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; - - @override - String get confirmAccountDeleteTitle => '确认删除账户'; - - @override - String get confirmAccountDeleteMessage => - '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; - - @override - String get delete => '删除'; - - @override - String get createNewAccount => '创建新账号'; - - @override - String get password => '密码'; - - @override - String get confirmPassword => '请确认密码'; - - @override - String passwordStrength(String passwordStrengthValue) { - return '密码强度: $passwordStrengthValue'; - } - - @override - String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; - - @override - String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; - - @override - String get signUpTerms => - '我同意 服务条款隐私政策'; - - @override - String get termsOfServicesTitle => '服务条款'; - - @override - String get privacyPolicyTitle => '隐私政策'; - - @override - String get ackPasswordLostWarning => - '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; - - @override - String get encryption => '加密'; - - @override - String get logInLabel => '登录'; - - @override - String get welcomeBack => '欢迎回来!'; - - @override - String get loginTerms => - '点击登录后,我同意 服务条款隐私政策'; - - @override - String get noInternetConnection => '无互联网连接'; - - @override - String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; - - @override - String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; - - @override - String get recreatePasswordTitle => '重新创建密码'; - - @override - String get recreatePasswordBody => - '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; - - @override - String get useRecoveryKey => '使用恢复密钥'; - - @override - String get forgotPassword => '忘记密码'; - - @override - String get changeEmail => '修改邮箱'; - - @override - String get verifyEmail => '验证电子邮件'; - - @override - String weHaveSendEmailTo(String email) { - return '我们已经发送邮件到 $email'; - } - - @override - String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; - - @override - String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; - - @override - String get tapToEnterCode => '点击以输入代码'; - - @override - String get sendEmail => '发送电子邮件'; - - @override - String get resendEmail => '重新发送电子邮件'; - - @override - String get passKeyPendingVerification => '仍需验证'; - - @override - String get loginSessionExpired => '会话已过期'; - - @override - String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; - - @override - String get passkeyAuthTitle => '通行密钥验证'; - - @override - String get waitingForVerification => '等待验证...'; - - @override - String get tryAgain => '请再试一次'; - - @override - String get checkStatus => '检查状态'; - - @override - String get loginWithTOTP => '使用 TOTP 登录'; - - @override - String get recoverAccount => '恢复账户'; - - @override - String get setPasswordTitle => '设置密码'; - - @override - String get changePasswordTitle => '修改密码'; - - @override - String get resetPasswordTitle => '重置密码'; - - @override - String get encryptionKeys => '加密密钥'; - - @override - String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; - - @override - String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; - - @override - String get passwordWarning => - '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; - - @override - String get howItWorks => '工作原理'; - - @override - String get generatingEncryptionKeys => '正在生成加密密钥...'; - - @override - String get passwordChangedSuccessfully => '密码修改成功'; - - @override - String get signOutFromOtherDevices => '从其他设备登出'; - - @override - String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; - - @override - String get signOutOtherDevices => '登出其他设备'; - - @override - String get doNotSignOut => '不要登出'; - - @override - String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; - - @override - String get continueLabel => '继续'; - - @override - String get insecureDevice => '设备不安全'; - - @override - String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; - - @override - String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; - - @override - String get recoveryKey => '恢复密钥'; - - @override - String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; - - @override - String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; - - @override - String get doThisLater => '稍后再做'; - - @override - String get saveKey => '保存密钥'; - - @override - String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; - - @override - String get noRecoveryKeyTitle => '没有恢复密钥吗?'; - - @override - String get twoFactorAuthTitle => '两步验证'; - - @override - String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; - - @override - String get lostDeviceTitle => '丢失了设备吗?'; - - @override - String get enterRecoveryKeyHint => '输入您的恢复密钥'; - - @override - String get recover => '恢复'; - - @override - String get loggingOut => '正在登出...'; - - @override - String get immediately => '立即'; - - @override - String get appLock => '应用锁'; - - @override - String get autoLock => '自动锁定'; - - @override - String get noSystemLockFound => '未找到系统锁'; - - @override - String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; - - @override - String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; - - @override - String get deviceLock => '设备锁'; - - @override - String get pinLock => 'Pin 锁定'; - - @override - String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; - - @override - String get hideContent => '隐藏内容'; - - @override - String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; - - @override - String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; - - @override - String get tooManyIncorrectAttempts => '错误的尝试次数过多'; - - @override - String get tapToUnlock => '点击解锁'; - - @override - String get areYouSureYouWantToLogout => '您确定要登出吗?'; - - @override - String get yesLogout => '是的,登出'; - - @override - String get authToViewSecrets => '请进行身份验证以查看您的密钥'; - - @override - String get next => '下一步'; - - @override - String get setNewPassword => '设置新密码'; - - @override - String get enterPin => '输入 PIN 码'; - - @override - String get setNewPin => '设置新 PIN 码'; - - @override - String get confirm => '确认'; - - @override - String get reEnterPassword => '再次输入密码'; - - @override - String get reEnterPin => '再次输入 PIN 码'; - - @override - String get androidBiometricHint => '验证身份'; - - @override - String get androidBiometricNotRecognized => '未能识别,请重试。'; - - @override - String get androidBiometricSuccess => '成功'; - - @override - String get androidCancelButton => '取消'; - - @override - String get androidSignInTitle => '需要进行身份验证'; - - @override - String get androidBiometricRequiredTitle => '需要进行生物识别认证'; - - @override - String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; - - @override - String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; - - @override - String get goToSettings => '前往设置'; - - @override - String get androidGoToSettingsDescription => - '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; - - @override - String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; - - @override - String get iOSOkButton => '好'; - - @override - String get emailAlreadyRegistered => '电子邮件地址已被注册。'; - - @override - String get emailNotRegistered => '电子邮件地址未注册。'; - - @override - String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; - - @override - String emailChangedTo(String newEmail) { - return '电子邮件已更改为 $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; - - @override - String get authenticationSuccessful => '认证成功!'; - - @override - String get sessionExpired => '会话已过期'; - - @override - String get incorrectRecoveryKey => '恢复密钥不正确'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; - - @override - String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; - - @override - String get yourVerificationCodeHasExpired => '您的验证码已过期'; - - @override - String get incorrectCode => '验证码错误'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; - - @override - String get developerSettings => '开发者设置'; - - @override - String get serverEndpoint => '服务器端点'; - - @override - String get invalidEndpoint => '端点无效'; - - @override - String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; - - @override - String get endpointUpdatedMessage => '端点更新成功'; + String get recover => 'Recover'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From e36831b599078d398cfeb9cd655795533f28fe0d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:38:15 +0530 Subject: [PATCH 076/164] Update strings --- .../lib/l10n/strings_localizations_fr.dart | 532 ------------------ 1 file changed, 532 deletions(-) diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 0f521ac6d1..54a58684ad 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -432,536 +432,4 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get recover => 'Recover'; - 'Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ?'; - - @override - String get enterNewEmailHint => 'Saisissez votre nouvelle adresse email'; - - @override - String get email => 'E-mail'; - - @override - String get verify => 'Vérifier'; - - @override - String get invalidEmailTitle => 'Adresse e-mail invalide'; - - @override - String get invalidEmailMessage => - 'Veuillez saisir une adresse e-mail valide.'; - - @override - String get pleaseWait => 'Veuillez patienter...'; - - @override - String get verifyPassword => 'Vérifier le mot de passe'; - - @override - String get incorrectPasswordTitle => 'Mot de passe incorrect'; - - @override - String get pleaseTryAgain => 'Veuillez réessayer'; - - @override - String get enterPassword => 'Saisissez le mot de passe'; - - @override - String get enterYourPasswordHint => 'Entrez votre mot de passe'; - - @override - String get activeSessions => 'Sessions actives'; - - @override - String get oops => 'Oups'; - - @override - String get somethingWentWrongPleaseTryAgain => - 'Quelque chose s\'est mal passé, veuillez recommencer'; - - @override - String get thisWillLogYouOutOfThisDevice => - 'Cela vous déconnectera de cet appareil !'; - - @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'Cela vous déconnectera de l\'appareil suivant :'; - - @override - String get terminateSession => 'Quitter la session ?'; - - @override - String get terminate => 'Quitter'; - - @override - String get thisDevice => 'Cet appareil'; - - @override - String get createAccount => 'Créer un compte'; - - @override - String get weakStrength => 'Faible'; - - @override - String get moderateStrength => 'Modéré'; - - @override - String get strongStrength => 'Fort'; - - @override - String get deleteAccount => 'Supprimer le compte'; - - @override - String get deleteAccountQuery => - 'Nous sommes désolés de vous voir partir. Rencontrez-vous un problème ?'; - - @override - String get yesSendFeedbackAction => 'Oui, envoyer un commentaire'; - - @override - String get noDeleteAccountAction => 'Non, supprimer le compte'; - - @override - String get initiateAccountDeleteTitle => - 'Veuillez vous authentifier pour débuter la suppression du compte'; - - @override - String get confirmAccountDeleteTitle => 'Confirmer la suppression du compte'; - - @override - String get confirmAccountDeleteMessage => - 'Ce compte est lié à d\'autres applications ente, si vous en utilisez une.\n\nVos données téléchargées, dans toutes les applications ente, seront planifiées pour suppression, et votre compte sera définitivement supprimé.'; - - @override - String get delete => 'Supprimer'; - - @override - String get createNewAccount => 'Créer un nouveau compte'; - - @override - String get password => 'Mot de passe'; - - @override - String get confirmPassword => 'Confirmer le mot de passe'; - - @override - String passwordStrength(String passwordStrengthValue) { - return 'Force du mot de passe : $passwordStrengthValue'; - } - - @override - String get hearUsWhereTitle => - 'Comment avez-vous entendu parler de Ente? (facultatif)'; - - @override - String get hearUsExplanation => - 'Nous ne suivons pas les installations d\'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !'; - - @override - String get signUpTerms => - 'J\'accepte les conditions d\'utilisation et la politique de confidentialité'; - - @override - String get termsOfServicesTitle => 'Conditions'; - - @override - String get privacyPolicyTitle => 'Politique de confidentialité'; - - @override - String get ackPasswordLostWarning => - 'Je comprends que si je perds mon mot de passe, je risque de perdre mes données puisque celles-ci sont chiffrées de bout en bout.'; - - @override - String get encryption => 'Chiffrement'; - - @override - String get logInLabel => 'Connexion'; - - @override - String get welcomeBack => 'Bon retour parmi nous !'; - - @override - String get loginTerms => - 'En cliquant sur \"Connexion\", j\'accepte les conditions d\'utilisation et la politique de confidentialité'; - - @override - String get noInternetConnection => 'Aucune connexion internet'; - - @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Veuillez vérifier votre connexion internet puis réessayer.'; - - @override - String get verificationFailedPleaseTryAgain => - 'La vérification a échouée, veuillez réessayer'; - - @override - String get recreatePasswordTitle => 'Recréer le mot de passe'; - - @override - String get recreatePasswordBody => - 'L\'appareil actuel n\'est pas assez puissant pour vérifier votre mot de passe, donc nous avons besoin de le régénérer une fois d\'une manière qu\'il fonctionne avec tous les périphériques.\n\nVeuillez vous connecter en utilisant votre clé de récupération et régénérer votre mot de passe (vous pouvez utiliser le même si vous le souhaitez).'; - - @override - String get useRecoveryKey => 'Utiliser la clé de récupération'; - - @override - String get forgotPassword => 'Mot de passe oublié'; - - @override - String get changeEmail => 'Modifier l\'e-mail'; - - @override - String get verifyEmail => 'Vérifier l\'e-mail'; - - @override - String weHaveSendEmailTo(String email) { - return 'Nous avons envoyé un mail à $email'; - } - - @override - String get toResetVerifyEmail => - 'Pour réinitialiser votre mot de passe, veuillez d\'abord vérifier votre e-mail.'; - - @override - String get checkInboxAndSpamFolder => - 'Veuillez consulter votre boîte de courriels (et les spam) pour compléter la vérification'; - - @override - String get tapToEnterCode => 'Appuyez pour entrer un code'; - - @override - String get sendEmail => 'Envoyer un e-mail'; - - @override - String get resendEmail => 'Renvoyer le courriel'; - - @override - String get passKeyPendingVerification => - 'La vérification est toujours en attente'; - - @override - String get loginSessionExpired => 'Session expirée'; - - @override - String get loginSessionExpiredDetails => - 'Votre session a expiré. Veuillez vous reconnecter.'; - - @override - String get passkeyAuthTitle => 'Vérification du code d\'accès'; - - @override - String get waitingForVerification => 'En attente de vérification...'; - - @override - String get tryAgain => 'Réessayer'; - - @override - String get checkStatus => 'Vérifier le statut'; - - @override - String get loginWithTOTP => 'Se connecter avec un code TOTP'; - - @override - String get recoverAccount => 'Récupérer un compte'; - - @override - String get setPasswordTitle => 'Définir le mot de passe'; - - @override - String get changePasswordTitle => 'Modifier le mot de passe'; - - @override - String get resetPasswordTitle => 'Réinitialiser le mot de passe'; - - @override - String get encryptionKeys => 'Clés de chiffrement'; - - @override - String get enterPasswordToEncrypt => - 'Entrez un mot de passe que nous pouvons utiliser pour chiffrer vos données'; - - @override - String get enterNewPasswordToEncrypt => - 'Entrez un nouveau mot de passe que nous pouvons utiliser pour chiffrer vos données'; - - @override - String get passwordWarning => - 'Nous ne stockons pas ce mot de passe. Si vous l\'oubliez, nous ne pourrons pas déchiffrer vos données'; - - @override - String get howItWorks => 'Comment ça fonctionne'; - - @override - String get generatingEncryptionKeys => - 'Génération des clés de chiffrement...'; - - @override - String get passwordChangedSuccessfully => - 'Le mot de passe a été modifié avec succès'; - - @override - String get signOutFromOtherDevices => 'Déconnexion des autres appareils'; - - @override - String get signOutOtherBody => - 'Si vous pensez que quelqu\'un connaît peut-être votre mot de passe, vous pouvez forcer tous les autres appareils utilisant votre compte à se déconnecter.'; - - @override - String get signOutOtherDevices => 'Déconnecter les autres appareils'; - - @override - String get doNotSignOut => 'Ne pas se déconnecter'; - - @override - String get generatingEncryptionKeysTitle => - 'Génération des clés de chiffrement...'; - - @override - String get continueLabel => 'Continuer'; - - @override - String get insecureDevice => 'Appareil non sécurisé'; - - @override - String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - '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.'; - - @override - String get recoveryKeyCopiedToClipboard => - 'Clé de récupération copiée dans le presse-papiers'; - - @override - String get recoveryKey => 'Clé de récupération'; - - @override - String get recoveryKeyOnForgotPassword => - 'Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.'; - - @override - String get recoveryKeySaveDescription => - 'Nous ne stockons pas cette clé, veuillez enregistrer cette clé de 24 mots dans un endroit sûr.'; - - @override - String get doThisLater => 'Plus tard'; - - @override - String get saveKey => 'Enregistrer la clé'; - - @override - String get recoveryKeySaved => - 'Clé de récupération enregistrée dans le dossier Téléchargements !'; - - @override - String get noRecoveryKeyTitle => 'Pas de clé de récupération ?'; - - @override - String get twoFactorAuthTitle => 'Authentification à deux facteurs'; - - @override - String get enterCodeHint => - 'Entrez le code à 6 chiffres de votre application d\'authentification'; - - @override - String get lostDeviceTitle => 'Appareil perdu ?'; - - @override - String get enterRecoveryKeyHint => 'Saisissez votre clé de récupération'; - - @override - String get recover => 'Restaurer'; - - @override - String get loggingOut => 'Deconnexion...'; - - @override - String get immediately => 'Immédiatement'; - - @override - String get appLock => 'Verrouillage d\'application'; - - @override - String get autoLock => 'Verrouillage automatique'; - - @override - String get noSystemLockFound => 'Aucun verrou système trouvé'; - - @override - String get deviceLockEnablePreSteps => - '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.'; - - @override - String get appLockDescription => - 'Choisissez entre l\'écran de verrouillage par défaut de votre appareil et un écran de verrouillage par code PIN ou mot de passe personnalisé.'; - - @override - String get deviceLock => 'Verrouillage de l\'appareil'; - - @override - String get pinLock => 'Verrouillage par code PIN'; - - @override - String get autoLockFeatureDescription => - 'Délai après lequel l\'application se verrouille une fois qu\'elle a été mise en arrière-plan'; - - @override - String get hideContent => 'Masquer le contenu'; - - @override - String get hideContentDescriptionAndroid => - 'Masque le contenu de l\'application sur le menu et désactive les captures d\'écran'; - - @override - String get hideContentDescriptioniOS => - 'Masque le contenu de l\'application sur le menu'; - - @override - String get tooManyIncorrectAttempts => 'Trop de tentatives incorrectes'; - - @override - String get tapToUnlock => 'Appuyer pour déverrouiller'; - - @override - String get areYouSureYouWantToLogout => - 'Êtes-vous sûr de vouloir vous déconnecter ?'; - - @override - String get yesLogout => 'Oui, se déconnecter'; - - @override - String get authToViewSecrets => - 'Veuillez vous authentifier pour voir vos souvenirs'; - - @override - String get next => 'Suivant'; - - @override - String get setNewPassword => 'Définir un nouveau mot de passe'; - - @override - String get enterPin => 'Saisir le code PIN'; - - @override - String get setNewPin => 'Définir un nouveau code PIN'; - - @override - String get confirm => 'Confirmer'; - - @override - String get reEnterPassword => 'Ressaisir le mot de passe'; - - @override - String get reEnterPin => 'Ressaisir le code PIN'; - - @override - String get androidBiometricHint => 'Vérifier l’identité'; - - @override - String get androidBiometricNotRecognized => - 'Non reconnu. Veuillez réessayer.'; - - @override - String get androidBiometricSuccess => 'Parfait'; - - @override - String get androidCancelButton => 'Annuler'; - - @override - String get androidSignInTitle => 'Authentification requise'; - - @override - String get androidBiometricRequiredTitle => 'Empreinte digitale requise'; - - @override - String get androidDeviceCredentialsRequiredTitle => - 'Identifiants de l\'appareil requis'; - - @override - String get androidDeviceCredentialsSetupDescription => - 'Identifiants de l\'appareil requis'; - - @override - String get goToSettings => 'Allez dans les paramètres'; - - @override - String get androidGoToSettingsDescription => - 'L\'authentification biométrique n\'est pas configurée sur votre appareil. Allez dans \'Paramètres > Sécurité\' pour l\'ajouter.'; - - @override - String get iOSLockOut => - 'L\'authentification biométrique est désactivée. Veuillez verrouiller et déverrouiller votre écran pour l\'activer.'; - - @override - String get iOSOkButton => 'Ok'; - - @override - String get emailAlreadyRegistered => 'E-mail déjà enregistré.'; - - @override - String get emailNotRegistered => 'E-mail non enregistré.'; - - @override - String get thisEmailIsAlreadyInUse => 'Cette adresse mail est déjà utilisé'; - - @override - String emailChangedTo(String newEmail) { - return 'L\'e-mail a été changé en $newEmail'; - } - - @override - String get authenticationFailedPleaseTryAgain => - 'L\'authentification a échouée, veuillez réessayer'; - - @override - String get authenticationSuccessful => 'Authentification réussie!'; - - @override - String get sessionExpired => 'Session expirée'; - - @override - String get incorrectRecoveryKey => 'Clé de récupération non valide'; - - @override - String get theRecoveryKeyYouEnteredIsIncorrect => - 'La clé de récupération que vous avez entrée est incorrecte'; - - @override - String get twofactorAuthenticationSuccessfullyReset => - 'L\'authentification à deux facteurs a été réinitialisée avec succès '; - - @override - String get noRecoveryKey => 'No recovery key'; - - @override - String get yourAccountHasBeenDeleted => 'Your account has been deleted'; - - @override - String get verificationId => 'Verification ID'; - - @override - String get yourVerificationCodeHasExpired => - 'Votre code de vérification a expiré'; - - @override - String get incorrectCode => 'Code non valide'; - - @override - String get sorryTheCodeYouveEnteredIsIncorrect => - 'Le code que vous avez saisi est incorrect'; - - @override - String get developerSettings => 'Paramètres du développeur'; - - @override - String get serverEndpoint => 'Point de terminaison serveur'; - - @override - String get invalidEndpoint => 'Point de terminaison non valide'; - - @override - String get invalidEndpointMessage => - 'Désolé, le point de terminaison que vous avez entré n\'est pas valide. Veuillez en entrer un valide puis réessayez.'; - - @override - String get endpointUpdatedMessage => - 'Point de terminaison mis à jour avec succès'; } From e352de8b9c62f297303a4a086785c907bb4ffb8c Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:38:15 +0530 Subject: [PATCH 077/164] Update strings From 7a91e714fe89409a06f8e816fbd88493e6157802 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:51:23 +0530 Subject: [PATCH 078/164] Setup common lockscreen --- mobile/packages/lock_screen/lib/auth_util.dart | 4 ++-- .../lib/local_authentication_service.dart | 10 +++++----- .../lock_screen/lib/lock_screen_settings.dart | 4 ++-- mobile/packages/lock_screen/lib/ui/app_lock.dart | 4 ++-- mobile/packages/lock_screen/lib/ui/lock_screen.dart | 6 +++--- .../lock_screen/lib/ui/lock_screen_auto_lock.dart | 2 +- .../lib/ui/lock_screen_confirm_password.dart | 2 +- .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 4 ++-- .../lock_screen/lib/ui/lock_screen_options.dart | 12 ++++++------ .../lock_screen/lib/ui/lock_screen_password.dart | 8 ++++---- .../lock_screen/lib/ui/lock_screen_pin.dart | 10 +++++----- mobile/packages/lock_screen/pubspec.lock | 9 ++++----- mobile/packages/lock_screen/pubspec.yaml | 13 +++++-------- 13 files changed, 42 insertions(+), 46 deletions(-) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index f4071b0712..e1574ab8db 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -1,13 +1,13 @@ import 'dart:io'; -import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; +import 'package:lock_screen/local_authentication_service.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index eb1a65cf05..2ff4b0b673 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -1,10 +1,5 @@ import 'dart:io'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; -import 'package:ente_lock_screen/ui/lock_screen_password.dart'; -import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:flutter/foundation.dart'; @@ -12,6 +7,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; +import 'package:lock_screen/auth_util.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:lock_screen/ui/app_lock.dart'; +import 'package:lock_screen/ui/lock_screen_password.dart'; +import 'package:lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/lock_screen_settings.dart b/mobile/packages/lock_screen/lib/lock_screen_settings.dart index 2c631bc931..99a41478bf 100644 --- a/mobile/packages/lock_screen/lib/lock_screen_settings.dart +++ b/mobile/packages/lock_screen/lib/lock_screen_settings.dart @@ -3,10 +3,10 @@ import "dart:io"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_utils/platform_util.dart"; import "package:ente_events/event_bus.dart"; import "package:ente_events/models/signed_out_event.dart"; -import "package:ente_utils/platform_util.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter_secure_storage/flutter_secure_storage.dart"; import "package:privacy_screen/privacy_screen.dart"; diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index 9098737b7f..4e93ce5ec1 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:flutter/material.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). @@ -144,8 +144,8 @@ class _AppLockState extends State with WidgetsBindingObserver { Widget get _lockScreen { return PopScope( - canPop: false, child: this.widget.lockScreen, + canPop: false, ); } diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index 270d201eba..74cbcfc554 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -3,15 +3,15 @@ import 'dart:math'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; +import 'package:lock_screen/auth_util.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index 16b5f45ffe..dfdc11b8f5 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -1,4 +1,3 @@ -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/captioned_text_widget.dart'; import 'package:ente_ui/components/divider_widget.dart'; @@ -8,6 +7,7 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; +import 'package:lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index eb9e8d6824..e6f8bc0ae2 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -1,4 +1,3 @@ -import "package:ente_lock_screen/lock_screen_settings.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; @@ -6,6 +5,7 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index ecb4418968..3f97324092 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -1,11 +1,11 @@ import "dart:io"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 176172af95..190e4c971f 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -1,12 +1,6 @@ import "dart:async"; import "dart:io"; -import "package:ente_lock_screen/local_authentication_service.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/app_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_pin.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/models/button_type.dart"; @@ -20,6 +14,12 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; +import "package:lock_screen/local_authentication_service.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/app_lock.dart"; +import "package:lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:lock_screen/ui/lock_screen_password.dart"; +import "package:lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index eb5f2b94ed..a010a327e6 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -1,16 +1,16 @@ import "dart:convert"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 603ebdd67d..6a2b6a858b 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -1,17 +1,17 @@ import "dart:convert"; import "dart:io"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/colors.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/theme/text_style.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; +import "package:lock_screen/lock_screen_settings.dart"; +import "package:lock_screen/ui/custom_pin_keypad.dart"; +import "package:lock_screen/ui/lock_screen_confirm_pin.dart"; +import "package:lock_screen/ui/lock_screen_options.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index adae66b006..29bd04fca3 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -409,11 +409,10 @@ packages: flutter_local_authentication: dependency: "direct main" description: - path: "." - ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - url: "https://github.com/eaceto/flutter_local_authentication" - source: git + name: flutter_local_authentication + sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd + url: "https://pub.dev" + source: hosted version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index 5f6f1529c1..cd611a61f4 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -1,4 +1,4 @@ -name: ente_lock_screen +name: lock_screen description: A Flutter package containing lock screen UI components and services for Ente apps version: 1.0.0 publish_to: none @@ -8,6 +8,8 @@ environment: flutter: ">=1.17.0" dependencies: + flutter: + sdk: flutter ente_accounts: path: ../accounts ente_configuration: @@ -23,13 +25,8 @@ dependencies: path: ../ui ente_utils: path: ../utils - flutter: - sdk: flutter flutter_animate: ^4.1.0 - flutter_local_authentication: - git: - url: https://github.com/eaceto/flutter_local_authentication - ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 + flutter_local_authentication: ^1.1.11 flutter_secure_storage: ^9.0.0 local_auth: ^2.1.8 logging: ^1.1.1 @@ -38,8 +35,8 @@ dependencies: shared_preferences: ^2.5.3 dev_dependencies: - flutter_lints: ^5.0.0 flutter_test: sdk: flutter + flutter_lints: ^5.0.0 flutter: From d69595e744d2e19a38c8dbd621c44d6a608162fa Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 14:51:23 +0530 Subject: [PATCH 079/164] Setup common lockscreen From 1f100566ad371eebe36d5f20f91573a4a7ab660a Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:01 +0530 Subject: [PATCH 080/164] Update common lockscreen --- mobile/packages/lock_screen/lib/auth_util.dart | 4 ++-- .../lib/local_authentication_service.dart | 10 +++++----- mobile/packages/lock_screen/lib/ui/app_lock.dart | 2 +- mobile/packages/lock_screen/lib/ui/lock_screen.dart | 6 +++--- .../lock_screen/lib/ui/lock_screen_auto_lock.dart | 2 +- .../lib/ui/lock_screen_confirm_password.dart | 2 +- .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 4 ++-- .../lock_screen/lib/ui/lock_screen_options.dart | 12 ++++++------ .../lock_screen/lib/ui/lock_screen_password.dart | 6 +++--- .../packages/lock_screen/lib/ui/lock_screen_pin.dart | 8 ++++---- mobile/packages/lock_screen/pubspec.yaml | 2 +- 11 files changed, 29 insertions(+), 29 deletions(-) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index e1574ab8db..396be4f809 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -6,8 +6,8 @@ import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:lock_screen/local_authentication_service.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index 2ff4b0b673..6ae795c61e 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -7,11 +7,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:lock_screen/auth_util.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; -import 'package:lock_screen/ui/app_lock.dart'; -import 'package:lock_screen/ui/lock_screen_password.dart'; -import 'package:lock_screen/ui/lock_screen_pin.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen_password.dart'; +import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index 4e93ce5ec1..f72fa1e8a5 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index 74cbcfc554..e89869b740 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -9,9 +9,9 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:lock_screen/auth_util.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; -import 'package:lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index dfdc11b8f5..1f0901ed50 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -7,7 +7,7 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import 'package:lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index e6f8bc0ae2..716a1ad7da 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -5,7 +5,7 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index 3f97324092..33ce921012 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -4,8 +4,8 @@ import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/custom_pin_keypad.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 190e4c971f..2bf9d21131 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -14,12 +14,12 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; -import "package:lock_screen/local_authentication_service.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/app_lock.dart"; -import "package:lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:lock_screen/ui/lock_screen_password.dart"; -import "package:lock_screen/ui/lock_screen_pin.dart"; +import "package:ente_lock_screen/local_authentication_service.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/app_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index a010a327e6..93a1c4d636 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -8,9 +8,9 @@ import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:lock_screen/ui/lock_screen_options.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 6a2b6a858b..2001cae468 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -8,10 +8,10 @@ import "package:ente_ui/theme/text_style.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:lock_screen/lock_screen_settings.dart"; -import "package:lock_screen/ui/custom_pin_keypad.dart"; -import "package:lock_screen/ui/lock_screen_confirm_pin.dart"; -import "package:lock_screen/ui/lock_screen_options.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index cd611a61f4..02b553a96f 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -1,4 +1,4 @@ -name: lock_screen +name: ente_lock_screen description: A Flutter package containing lock screen UI components and services for Ente apps version: 1.0.0 publish_to: none From 016d646971135a37403296fbc9387c62566882f1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:09 +0530 Subject: [PATCH 081/164] Update common ui From 81e926ef2df04e3633808d9dcd7173b037843d55 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 082/164] Setup common accounts package --- mobile/packages/accounts/analysis_options.yaml | 3 ++- mobile/packages/accounts/pubspec.lock | 9 ++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index 1bd78bc1b0..60bc1eb398 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1,8 +1,9 @@ +include: package:flutter_lints/flutter.yaml + # For more linters, we can check https://dart-lang.github.io/linter/lints/index.html # or https://pub.dev/packages/lint (Effective dart) # use "flutter analyze ." or "dart analyze ." for running lint checks -include: package:flutter_lints/flutter.yaml linter: rules: # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 1dd9150ce5..8fee60bb47 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -409,11 +409,10 @@ packages: flutter_local_authentication: dependency: transitive description: - path: "." - ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" - url: "https://github.com/eaceto/flutter_local_authentication" - source: git + name: flutter_local_authentication + sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd + url: "https://pub.dev" + source: hosted version: "1.2.0" flutter_localizations: dependency: transitive From 3019d858c939125a2f0098cf47589ac07ace93d6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 083/164] Setup common accounts package --- .../packages/accounts/analysis_options.yaml | 72 ------------------- .../packages/accounts/lib/ente_accounts.dart | 21 +++--- .../accounts/lib/models/user_details.dart | 4 +- .../lib/pages/change_email_dialog.dart | 2 +- .../lib/pages/delete_account_page.dart | 14 ++-- .../accounts/lib/pages/login_page.dart | 2 +- .../pages/login_pwd_verification_page.dart | 4 +- .../lib/pages/ott_verification_page.dart | 2 +- .../accounts/lib/pages/passkey_page.dart | 2 +- .../lib/pages/password_entry_page.dart | 6 +- .../lib/pages/password_reentry_page.dart | 24 +++---- .../accounts/lib/pages/recovery_key_page.dart | 2 +- .../accounts/lib/pages/recovery_page.dart | 6 +- .../pages/request_pwd_verification_page.dart | 4 +- .../accounts/lib/pages/sessions_page.dart | 4 +- .../pages/two_factor_authentication_page.dart | 4 +- .../lib/pages/two_factor_recovery_page.dart | 2 +- .../accounts/lib/services/user_service.dart | 37 +++------- mobile/packages/accounts/pubspec.yaml | 58 ++++++++------- 19 files changed, 92 insertions(+), 178 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index 60bc1eb398..f9b303465f 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1,73 +1 @@ include: package:flutter_lints/flutter.yaml - -# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html -# or https://pub.dev/packages/lint (Effective dart) -# use "flutter analyze ." or "dart analyze ." for running lint checks - -linter: - rules: - # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml - # Ref https://dart-lang.github.io/linter/lints/ - - avoid_print - - avoid_unnecessary_containers - - avoid_web_libraries_in_flutter - - no_logic_in_create_state - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_final_locals - - require_trailing_commas - - sized_box_for_whitespace - - use_full_hex_values_for_flutter_colors - - use_key_in_widget_constructors - - cancel_subscriptions - - - - avoid_empty_else - - exhaustive_cases - - # just style suggestions - - sort_pub_dependencies - - use_rethrow_when_possible - - prefer_double_quotes - - directives_ordering - - always_use_package_imports - - sort_child_properties_last - - unawaited_futures - -analyzer: - errors: - avoid_empty_else: error - exhaustive_cases: error - curly_braces_in_flow_control_structures: error - directives_ordering: error - require_trailing_commas: error - always_use_package_imports: warning - prefer_final_fields: error - unused_import: error - camel_case_types: error - prefer_is_empty: warning - use_rethrow_when_possible: info - unused_field: warning - use_key_in_widget_constructors: warning - sort_child_properties_last: warning - sort_pub_dependencies: warning - library_private_types_in_public_api: warning - constant_identifier_names: ignore - prefer_const_constructors: warning - prefer_const_declarations: warning - prefer_const_constructors_in_immutables: warning - prefer_final_locals: warning - unnecessary_const: error - cancel_subscriptions: error - unrelated_type_equality_checks: error - unnecessary_cast: info - - - unawaited_futures: warning # convert to warning after fixing existing issues - invalid_dependency: info - use_build_context_synchronously: ignore # experimental lint, requires many changes - prefer_interpolation_to_compose_strings: ignore # later too many warnings - prefer_double_quotes: ignore # too many warnings - avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index 9fcaf2bcf6..e7865b2f1b 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,13 +1,21 @@ -export 'models/bonus.dart'; -export 'models/delete_account.dart'; +/// A Flutter package containing account-related functionality for Ente apps +library ente_accounts; + +// Models +export 'models/user_details.dart'; export 'models/sessions.dart'; +export 'models/two_factor.dart'; +export 'models/srp.dart'; +export 'models/delete_account.dart'; export 'models/set_keys_request.dart'; export 'models/set_recovery_key_request.dart'; -export 'models/srp.dart'; +export 'models/bonus.dart'; export 'models/subscription.dart'; -export 'models/two_factor.dart'; -export 'models/user_details.dart'; +// Services +export 'services/user_service.dart'; + +// Pages export 'pages/change_email_dialog.dart'; export 'pages/delete_account_page.dart'; export 'pages/email_entry_page.dart'; @@ -23,6 +31,3 @@ export 'pages/request_pwd_verification_page.dart'; export 'pages/sessions_page.dart'; export 'pages/two_factor_authentication_page.dart'; export 'pages/two_factor_recovery_page.dart'; - -export 'services/passkey_service.dart'; -export 'services/user_service.dart'; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index 7fe5f8e60f..bae0311a7f 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:math'; -import 'package:ente_accounts/models/bonus.dart'; -import 'package:ente_accounts/models/subscription.dart'; +import 'bonus.dart'; +import 'subscription.dart'; class UserDetails { final String email; diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index 860b627918..0cc49f25bc 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class ChangeEmailDialog extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index f79e32cac8..b06ac5e4d5 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -4,19 +4,19 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_ui/components/buttons/gradient_button.dart'; -import 'package:ente_ui/components/dialogs.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; class DeleteAccountPage extends StatelessWidget { - final BaseConfiguration config; + final BaseConfiguration configuration; const DeleteAccountPage( - this.config, { + this.configuration, { super.key, }); @@ -169,9 +169,9 @@ class DeleteAccountPage extends StatelessWidget { final decryptChallenge = CryptoUtil.openSealSync( CryptoUtil.base642bin(response.encryptedChallenge), CryptoUtil.base642bin( - config.getKeyAttributes()!.publicKey, + configuration.getKeyAttributes()!.publicKey, ), - config.getSecretKey()!, + configuration.getSecretKey()!, ); final challengeResponseStr = utf8.decode(decryptChallenge); await UserService.instance.deleteAccount(context, challengeResponseStr); diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index c51b74e8a4..17737cc0e0 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -2,10 +2,10 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import "package:ente_strings/ente_strings.dart"; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; +import "package:ente_strings/ente_strings.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import "package:styled_text/styled_text.dart"; diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart index e23064d389..6c0144f3b0 100644 --- a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -1,13 +1,13 @@ import "package:dio/dio.dart"; import "package:ente_accounts/ente_accounts.dart"; import "package:ente_configuration/base_configuration.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; import "package:ente_utils/email_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart index cc49d1f88d..442401fbd7 100644 --- a/mobile/packages/accounts/lib/pages/ott_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; import 'package:styled_text/styled_text.dart'; diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart index f7470939da..584be1bceb 100644 --- a/mobile/packages/accounts/lib/pages/passkey_page.dart +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -4,12 +4,12 @@ import 'package:app_links/app_links.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index 36152d883b..bf171e3cc3 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -1,6 +1,5 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/pages/base_home_page.dart'; @@ -9,6 +8,7 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; import 'package:ente_utils/platform_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; @@ -448,9 +448,7 @@ class _PasswordEntryPageState extends State { bool usingVolatilePassword = false, }) async { final dialog = createProgressDialog( - context, - context.strings.generatingEncryptionKeysTitle, - ); + context, context.strings.generatingEncryptionKeysTitle); await dialog.show(); try { if (usingVolatilePassword) { diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index f684fc1379..0de41c1992 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -4,25 +4,22 @@ import 'dart:typed_data'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; import 'package:flutter/material.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:logging/logging.dart'; -class PasswordReentryPage extends StatefulWidget { - final BaseConfiguration config; - final BaseHomePage homePage; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; - const PasswordReentryPage( - this.config, - this.homePage, { - super.key, - }); +class PasswordReentryPage extends StatefulWidget { + final BaseHomePage homePage; + final BaseConfiguration config; + + const PasswordReentryPage(this.homePage, this.config, {super.key}); @override State createState() => _PasswordReentryPageState(); @@ -126,10 +123,7 @@ class _PasswordReentryPageState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return RecoveryPage( - widget.config, - widget.homePage, - ); + return RecoveryPage(widget.homePage, widget.config); }, ), ); @@ -279,8 +273,8 @@ class _PasswordReentryPageState extends State { MaterialPageRoute( builder: (BuildContext context) { return RecoveryPage( - widget.config, widget.homePage, + widget.config, ); }, ), diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 3cf2249771..20f49b8f52 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -5,12 +5,12 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:dotted_border/dotted_border.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index 93d22991bf..0901452a11 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -1,17 +1,17 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { - final BaseConfiguration config; final BaseHomePage homePage; + final BaseConfiguration config; - const RecoveryPage(this.config, this.homePage, {super.key}); + const RecoveryPage(this.homePage, this.config, {super.key}); @override State createState() => _RecoveryPageState(); diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart index d3d23a1096..0fa74243ad 100644 --- a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -2,11 +2,11 @@ import "dart:convert"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; +import "package:ente_strings/ente_strings.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart index dbbcb9bf98..29774497ee 100644 --- a/mobile/packages/accounts/lib/pages/sessions_page.dart +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -1,13 +1,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/loading_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_utils/date_time_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:ente_utils/date_time_util.dart'; class SessionsPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart index f1fa4f327e..58a7312e5a 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_ui/lifecycle_event_handler.dart'; import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinput/pinput.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; class TwoFactorAuthenticationPage extends StatefulWidget { final String sessionID; diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart index 878a8a2ba8..12c6da59aa 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class TwoFactorRecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index 7bc0a54c64..8af3d55973 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -20,19 +20,19 @@ import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_accounts/pages/recovery_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; import 'package:ente_network/network.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/progress_dialog.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; @@ -348,10 +348,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage( - _config, - _homePage, - ); + return PasswordReentryPage(_homePage, _config); }, ), (route) => route.isFirst, @@ -429,15 +426,9 @@ class UserService { await _saveConfiguration(response); if (_config.getEncryptedToken() != null) { if (isResettingPasswordScreen) { - page = RecoveryPage( - _config, - _homePage, - ); + page = RecoveryPage(_homePage, _config); } else { - page = PasswordReentryPage( - _config, - _homePage, - ); + page = PasswordReentryPage(_homePage, _config); } } else { page = PasswordEntryPage( @@ -846,10 +837,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage( - _config, - _homePage, - ); + return PasswordReentryPage(_homePage, _config); }, ), (route) => route.isFirst, @@ -1013,10 +1001,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage( - _config, - _homePage, - ); + return PasswordReentryPage(_homePage, _config); }, ), (route) => route.isFirst, diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index 1d4064ac75..eca03a2a01 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -7,50 +7,54 @@ environment: flutter: ">=1.17.0" dependencies: - app_links: ^6.3.3 - bip39: ^1.0.6 - collection: ^1.18.0 - dio: ^5.4.0 - dotted_border: ^3.1.0 - email_validator: ^3.0.0 - ente_base: - path: ../base - ente_configuration: - path: ../configuration - ente_crypto_dart: - git: - url: https://github.com/ente-io/ente_crypto_dart.git - ente_events: - path: ../events - ente_lock_screen: - path: ../lock_screen - ente_network: - path: ../network + flutter: + sdk: flutter + + # Ente packages ente_strings: path: ../strings ente_ui: path: ../ui ente_utils: path: ../utils - file_saver: ^0.3.0 - flutter: - sdk: flutter + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_network: + path: ../network + ente_events: + path: ../events + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_lock_screen: + path: ../lock_screen + + # Third-party dependencies + bip39: ^1.0.6 + dio: ^5.4.0 logging: ^1.2.0 - password_strength: ^0.2.0 - pinput: ^5.0.1 pointycastle: ^3.7.3 - share_plus: ^11.0.0 shared_preferences: ^2.2.2 + file_saver: ^0.3.0 + share_plus: ^11.0.0 + uuid: ^4.2.1 + collection: ^1.18.0 + dotted_border: ^3.1.0 + password_strength: ^0.2.0 step_progress_indicator: ^1.0.2 styled_text: ^8.1.0 + email_validator: ^3.0.0 + pinput: ^5.0.1 + app_links: ^6.3.3 url_launcher: ^6.3.1 url_launcher_ios: ^6.3.1 - uuid: ^4.2.1 dev_dependencies: - flutter_lints: ^5.0.0 flutter_test: sdk: flutter + flutter_lints: ^5.0.0 flutter: From 3aaf11ba1d59a735bdbe2f71667451e1afd025d1 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:09:19 +0530 Subject: [PATCH 084/164] Setup common accounts package From be850c27c60cd74607ca6f14dba9e92d08d49c59 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:14:45 +0530 Subject: [PATCH 085/164] Fix dependency --- mobile/packages/lock_screen/pubspec.lock | 9 +++++---- mobile/packages/lock_screen/pubspec.yaml | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index 29bd04fca3..adae66b006 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -409,10 +409,11 @@ packages: flutter_local_authentication: dependency: "direct main" description: - name: flutter_local_authentication - sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd - url: "https://pub.dev" - source: hosted + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index 02b553a96f..e5906cfc6a 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -26,7 +26,10 @@ dependencies: ente_utils: path: ../utils flutter_animate: ^4.1.0 - flutter_local_authentication: ^1.1.11 + flutter_local_authentication: + git: + url: https://github.com/eaceto/flutter_local_authentication + ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 flutter_secure_storage: ^9.0.0 local_auth: ^2.1.8 logging: ^1.1.1 From 694e3ca121576b6a1036fd344f1e47e344600b07 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 15:44:06 +0530 Subject: [PATCH 086/164] Remove changelog From f971835ae7ae11535fbd96b1257d170c64cd8e57 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:02:39 +0530 Subject: [PATCH 087/164] Remove noise From 0ff8184f47df517bf81df2f7ac34d5530b240910 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:12:57 +0530 Subject: [PATCH 088/164] Lint accounts --- .../packages/accounts/analysis_options.yaml | 71 +++++++++++++++++++ .../packages/accounts/lib/ente_accounts.dart | 18 +++-- .../accounts/lib/models/user_details.dart | 4 +- .../lib/pages/change_email_dialog.dart | 2 +- .../lib/pages/delete_account_page.dart | 6 +- .../accounts/lib/pages/login_page.dart | 2 +- .../pages/login_pwd_verification_page.dart | 4 +- .../lib/pages/ott_verification_page.dart | 2 +- .../accounts/lib/pages/passkey_page.dart | 2 +- .../lib/pages/password_entry_page.dart | 6 +- .../lib/pages/password_reentry_page.dart | 5 +- .../accounts/lib/pages/recovery_key_page.dart | 2 +- .../accounts/lib/pages/recovery_page.dart | 2 +- .../pages/request_pwd_verification_page.dart | 4 +- .../accounts/lib/pages/sessions_page.dart | 4 +- .../pages/two_factor_authentication_page.dart | 4 +- .../lib/pages/two_factor_recovery_page.dart | 2 +- .../accounts/lib/services/user_service.dart | 12 ++-- mobile/packages/accounts/pubspec.lock | 9 +-- mobile/packages/accounts/pubspec.yaml | 60 ++++++++-------- 20 files changed, 144 insertions(+), 77 deletions(-) diff --git a/mobile/packages/accounts/analysis_options.yaml b/mobile/packages/accounts/analysis_options.yaml index f9b303465f..1bd78bc1b0 100644 --- a/mobile/packages/accounts/analysis_options.yaml +++ b/mobile/packages/accounts/analysis_options.yaml @@ -1 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index e7865b2f1b..858677e3f9 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,21 +1,16 @@ /// A Flutter package containing account-related functionality for Ente apps library ente_accounts; -// Models -export 'models/user_details.dart'; -export 'models/sessions.dart'; -export 'models/two_factor.dart'; -export 'models/srp.dart'; +export 'models/bonus.dart'; export 'models/delete_account.dart'; +export 'models/sessions.dart'; export 'models/set_keys_request.dart'; export 'models/set_recovery_key_request.dart'; -export 'models/bonus.dart'; +export 'models/srp.dart'; export 'models/subscription.dart'; +export 'models/two_factor.dart'; +export 'models/user_details.dart'; -// Services -export 'services/user_service.dart'; - -// Pages export 'pages/change_email_dialog.dart'; export 'pages/delete_account_page.dart'; export 'pages/email_entry_page.dart'; @@ -31,3 +26,6 @@ export 'pages/request_pwd_verification_page.dart'; export 'pages/sessions_page.dart'; export 'pages/two_factor_authentication_page.dart'; export 'pages/two_factor_recovery_page.dart'; + +export 'services/passkey_service.dart'; +export 'services/user_service.dart'; diff --git a/mobile/packages/accounts/lib/models/user_details.dart b/mobile/packages/accounts/lib/models/user_details.dart index bae0311a7f..7fe5f8e60f 100644 --- a/mobile/packages/accounts/lib/models/user_details.dart +++ b/mobile/packages/accounts/lib/models/user_details.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'dart:math'; -import 'bonus.dart'; -import 'subscription.dart'; +import 'package:ente_accounts/models/bonus.dart'; +import 'package:ente_accounts/models/subscription.dart'; class UserDetails { final String email; diff --git a/mobile/packages/accounts/lib/pages/change_email_dialog.dart b/mobile/packages/accounts/lib/pages/change_email_dialog.dart index 0cc49f25bc..860b627918 100644 --- a/mobile/packages/accounts/lib/pages/change_email_dialog.dart +++ b/mobile/packages/accounts/lib/pages/change_email_dialog.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class ChangeEmailDialog extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index b06ac5e4d5..66963914d7 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -4,13 +4,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/dialogs.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; -import 'package:ente_ui/components/buttons/gradient_button.dart'; -import 'package:ente_ui/components/dialogs.dart'; class DeleteAccountPage extends StatelessWidget { final BaseConfiguration configuration; diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index 17737cc0e0..c51b74e8a4 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -2,10 +2,10 @@ import 'package:email_validator/email_validator.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import "package:ente_strings/ente_strings.dart"; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; -import "package:ente_strings/ente_strings.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import "package:styled_text/styled_text.dart"; diff --git a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart index 6c0144f3b0..e23064d389 100644 --- a/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/login_pwd_verification_page.dart @@ -1,13 +1,13 @@ import "package:dio/dio.dart"; import "package:ente_accounts/ente_accounts.dart"; import "package:ente_configuration/base_configuration.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; import "package:ente_utils/email_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/ott_verification_page.dart b/mobile/packages/accounts/lib/pages/ott_verification_page.dart index 442401fbd7..cc49d1f88d 100644 --- a/mobile/packages/accounts/lib/pages/ott_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/ott_verification_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/theme/ente_theme.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:step_progress_indicator/step_progress_indicator.dart'; import 'package:styled_text/styled_text.dart'; diff --git a/mobile/packages/accounts/lib/pages/passkey_page.dart b/mobile/packages/accounts/lib/pages/passkey_page.dart index 584be1bceb..f7470939da 100644 --- a/mobile/packages/accounts/lib/pages/passkey_page.dart +++ b/mobile/packages/accounts/lib/pages/passkey_page.dart @@ -4,12 +4,12 @@ import 'package:app_links/app_links.dart'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; import 'package:url_launcher/url_launcher_string.dart'; diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index bf171e3cc3..36152d883b 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -1,5 +1,6 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/pages/base_home_page.dart'; @@ -8,7 +9,6 @@ import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/navigation_util.dart'; import 'package:ente_utils/platform_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; @@ -448,7 +448,9 @@ class _PasswordEntryPageState extends State { bool usingVolatilePassword = false, }) async { final dialog = createProgressDialog( - context, context.strings.generatingEncryptionKeysTitle); + context, + context.strings.generatingEncryptionKeysTitle, + ); await dialog.show(); try { if (usingVolatilePassword) { diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 0de41c1992..7e081c9a5c 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -4,17 +4,16 @@ import 'dart:typed_data'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/button_widget.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_utils/email_util.dart'; import 'package:flutter/material.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:logging/logging.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; - class PasswordReentryPage extends StatefulWidget { final BaseHomePage homePage; final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 20f49b8f52..3cf2249771 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -5,12 +5,12 @@ import 'package:bip39/bip39.dart' as bip39; import 'package:dotted_border/dotted_border.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index 0901452a11..cc889bc34c 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/dynamic_fab.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart index 0fa74243ad..d3d23a1096 100644 --- a/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart +++ b/mobile/packages/accounts/lib/pages/request_pwd_verification_page.dart @@ -2,11 +2,11 @@ import "dart:convert"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_ui/utils/dialog_util.dart"; -import "package:ente_strings/ente_strings.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import 'package:flutter/material.dart'; import "package:logging/logging.dart"; diff --git a/mobile/packages/accounts/lib/pages/sessions_page.dart b/mobile/packages/accounts/lib/pages/sessions_page.dart index 29774497ee..dbbcb9bf98 100644 --- a/mobile/packages/accounts/lib/pages/sessions_page.dart +++ b/mobile/packages/accounts/lib/pages/sessions_page.dart @@ -1,13 +1,13 @@ import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/loading_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_utils/date_time_util.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:ente_utils/date_time_util.dart'; class SessionsPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart index 58a7312e5a..f1fa4f327e 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_authentication_page.dart @@ -1,10 +1,10 @@ import 'package:ente_accounts/ente_accounts.dart'; -import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_strings/ente_strings.dart'; +import 'package:ente_ui/lifecycle_event_handler.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:pinput/pinput.dart'; -import 'package:ente_ui/lifecycle_event_handler.dart'; class TwoFactorAuthenticationPage extends StatefulWidget { final String sessionID; diff --git a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart index 12c6da59aa..878a8a2ba8 100644 --- a/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/two_factor_recovery_page.dart @@ -1,7 +1,7 @@ import 'package:ente_accounts/ente_accounts.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/email_util.dart'; -import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/material.dart'; class TwoFactorRecoveryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index 8af3d55973..f127fe50be 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -20,19 +20,19 @@ import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_accounts/pages/recovery_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; +import 'package:ente_base/models/key_attributes.dart'; +import 'package:ente_base/models/key_gen_result.dart'; import 'package:ente_configuration/base_configuration.dart'; import 'package:ente_configuration/constants.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/user_details_changed_event.dart'; import 'package:ente_network/network.dart'; +import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/progress_dialog.dart'; import 'package:ente_ui/pages/base_home_page.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; -import 'package:ente_strings/ente_strings.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; import "package:flutter/foundation.dart"; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 8fee60bb47..1dd9150ce5 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -409,10 +409,11 @@ packages: flutter_local_authentication: dependency: transitive description: - name: flutter_local_authentication - sha256: eb2e471ba77fbc42b6ce8b358939dc9878dc71fcce5a482a8ddcb045563d87cd - url: "https://pub.dev" - source: hosted + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git version: "1.2.0" flutter_localizations: dependency: transitive diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index eca03a2a01..1d4064ac75 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -7,54 +7,50 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter - - # Ente packages + app_links: ^6.3.3 + bip39: ^1.0.6 + collection: ^1.18.0 + dio: ^5.4.0 + dotted_border: ^3.1.0 + email_validator: ^3.0.0 + ente_base: + path: ../base + ente_configuration: + path: ../configuration + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../events + ente_lock_screen: + path: ../lock_screen + ente_network: + path: ../network ente_strings: path: ../strings ente_ui: path: ../ui ente_utils: path: ../utils - ente_base: - path: ../base - ente_configuration: - path: ../configuration - ente_network: - path: ../network - ente_events: - path: ../events - ente_crypto_dart: - git: - url: https://github.com/ente-io/ente_crypto_dart.git - ente_lock_screen: - path: ../lock_screen - - # Third-party dependencies - bip39: ^1.0.6 - dio: ^5.4.0 - logging: ^1.2.0 - pointycastle: ^3.7.3 - shared_preferences: ^2.2.2 file_saver: ^0.3.0 - share_plus: ^11.0.0 - uuid: ^4.2.1 - collection: ^1.18.0 - dotted_border: ^3.1.0 + flutter: + sdk: flutter + logging: ^1.2.0 password_strength: ^0.2.0 + pinput: ^5.0.1 + pointycastle: ^3.7.3 + share_plus: ^11.0.0 + shared_preferences: ^2.2.2 step_progress_indicator: ^1.0.2 styled_text: ^8.1.0 - email_validator: ^3.0.0 - pinput: ^5.0.1 - app_links: ^6.3.3 url_launcher: ^6.3.1 url_launcher_ios: ^6.3.1 + uuid: ^4.2.1 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 053d0cfcaab42c590c80976d36d223341e514271 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:17 +0530 Subject: [PATCH 089/164] Fix minor lint --- mobile/packages/accounts/lib/ente_accounts.dart | 3 --- 1 file changed, 3 deletions(-) diff --git a/mobile/packages/accounts/lib/ente_accounts.dart b/mobile/packages/accounts/lib/ente_accounts.dart index 858677e3f9..9fcaf2bcf6 100644 --- a/mobile/packages/accounts/lib/ente_accounts.dart +++ b/mobile/packages/accounts/lib/ente_accounts.dart @@ -1,6 +1,3 @@ -/// A Flutter package containing account-related functionality for Ente apps -library ente_accounts; - export 'models/bonus.dart'; export 'models/delete_account.dart'; export 'models/sessions.dart'; From f2be25667f509a684724c47822221b3466e7b7d0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:14:28 +0530 Subject: [PATCH 090/164] Lint base From 5574fd748e08ff97d60be97e5e9415cc94c18d5f Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:15:23 +0530 Subject: [PATCH 091/164] Lint configuration From 9a357a716dcfc5c249c30edc18ce5e7b470b5695 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:16:24 +0530 Subject: [PATCH 092/164] Lint events From c2922a0cb267bd40fed370c8300c40389bf7166d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:21:53 +0530 Subject: [PATCH 093/164] Lint lock_screen --- mobile/packages/lock_screen/lib/auth_util.dart | 4 ++-- .../lib/local_authentication_service.dart | 10 +++++----- .../lock_screen/lib/lock_screen_settings.dart | 4 ++-- mobile/packages/lock_screen/lib/ui/app_lock.dart | 4 ++-- mobile/packages/lock_screen/lib/ui/lock_screen.dart | 6 +++--- .../lock_screen/lib/ui/lock_screen_auto_lock.dart | 2 +- .../lib/ui/lock_screen_confirm_password.dart | 2 +- .../lock_screen/lib/ui/lock_screen_confirm_pin.dart | 4 ++-- .../lock_screen/lib/ui/lock_screen_options.dart | 12 ++++++------ .../lock_screen/lib/ui/lock_screen_password.dart | 8 ++++---- .../packages/lock_screen/lib/ui/lock_screen_pin.dart | 12 ++++++------ mobile/packages/lock_screen/pubspec.yaml | 6 +++--- 12 files changed, 37 insertions(+), 37 deletions(-) diff --git a/mobile/packages/lock_screen/lib/auth_util.dart b/mobile/packages/lock_screen/lib/auth_util.dart index 396be4f809..f4071b0712 100644 --- a/mobile/packages/lock_screen/lib/auth_util.dart +++ b/mobile/packages/lock_screen/lib/auth_util.dart @@ -1,13 +1,13 @@ import 'dart:io'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; import 'package:local_auth_android/local_auth_android.dart'; import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:logging/logging.dart'; Future requestAuthentication( diff --git a/mobile/packages/lock_screen/lib/local_authentication_service.dart b/mobile/packages/lock_screen/lib/local_authentication_service.dart index 6ae795c61e..eb1a65cf05 100644 --- a/mobile/packages/lock_screen/lib/local_authentication_service.dart +++ b/mobile/packages/lock_screen/lib/local_authentication_service.dart @@ -1,5 +1,10 @@ import 'dart:io'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen_password.dart'; +import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:ente_ui/utils/toast_util.dart'; import 'package:flutter/foundation.dart'; @@ -7,11 +12,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_local_authentication/flutter_local_authentication.dart'; import 'package:local_auth/local_auth.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; -import 'package:ente_lock_screen/ui/lock_screen_password.dart'; -import 'package:ente_lock_screen/ui/lock_screen_pin.dart'; import 'package:logging/logging.dart'; class LocalAuthenticationService { diff --git a/mobile/packages/lock_screen/lib/lock_screen_settings.dart b/mobile/packages/lock_screen/lib/lock_screen_settings.dart index 99a41478bf..2c631bc931 100644 --- a/mobile/packages/lock_screen/lib/lock_screen_settings.dart +++ b/mobile/packages/lock_screen/lib/lock_screen_settings.dart @@ -3,10 +3,10 @@ import "dart:io"; import "dart:typed_data"; import "package:ente_configuration/base_configuration.dart"; -import "package:ente_utils/platform_util.dart"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:ente_events/event_bus.dart"; import "package:ente_events/models/signed_out_event.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; import "package:flutter_secure_storage/flutter_secure_storage.dart"; import "package:privacy_screen/privacy_screen.dart"; diff --git a/mobile/packages/lock_screen/lib/ui/app_lock.dart b/mobile/packages/lock_screen/lib/ui/app_lock.dart index f72fa1e8a5..9098737b7f 100644 --- a/mobile/packages/lock_screen/lib/ui/app_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/app_lock.dart @@ -1,7 +1,7 @@ import 'dart:async'; -import 'package:flutter/material.dart'; import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:flutter/material.dart'; /// A widget which handles app lifecycle events for showing and hiding a lock screen. /// This should wrap around a `MyApp` widget (or equivalent). @@ -144,8 +144,8 @@ class _AppLockState extends State with WidgetsBindingObserver { Widget get _lockScreen { return PopScope( - child: this.widget.lockScreen, canPop: false, + child: this.widget.lockScreen, ); } diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen.dart b/mobile/packages/lock_screen/lib/ui/lock_screen.dart index e89869b740..270d201eba 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen.dart @@ -3,15 +3,15 @@ import 'dart:math'; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_configuration/base_configuration.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_ui/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_animate/flutter_animate.dart'; -import 'package:ente_lock_screen/auth_util.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; -import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:logging/logging.dart'; class LockScreen extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart index 1f0901ed50..16b5f45ffe 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_auto_lock.dart @@ -1,3 +1,4 @@ +import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/captioned_text_widget.dart'; import 'package:ente_ui/components/divider_widget.dart'; @@ -7,7 +8,6 @@ import 'package:ente_ui/components/title_bar_title_widget.dart'; import 'package:ente_ui/components/title_bar_widget.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; -import 'package:ente_lock_screen/lock_screen_settings.dart'; class LockScreenAutoLock extends StatefulWidget { const LockScreenAutoLock({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart index 716a1ad7da..eb9e8d6824 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_password.dart @@ -1,3 +1,4 @@ +import "package:ente_lock_screen/lock_screen_settings.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; @@ -5,7 +6,6 @@ import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; class LockScreenConfirmPassword extends StatefulWidget { const LockScreenConfirmPassword({ diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index 33ce921012..ecb4418968 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -1,11 +1,11 @@ import "dart:io"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:pinput/pinput.dart"; class LockScreenConfirmPin extends StatefulWidget { diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart index 2bf9d21131..176172af95 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_options.dart @@ -1,6 +1,12 @@ import "dart:async"; import "dart:io"; +import "package:ente_lock_screen/local_authentication_service.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/app_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; +import "package:ente_lock_screen/ui/lock_screen_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_pin.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/button_widget.dart"; import "package:ente_ui/components/buttons/models/button_type.dart"; @@ -14,12 +20,6 @@ import "package:ente_ui/components/toggle_switch_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; import "package:ente_utils/platform_util.dart"; import "package:flutter/material.dart"; -import "package:ente_lock_screen/local_authentication_service.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/app_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_auto_lock.dart"; -import "package:ente_lock_screen/ui/lock_screen_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_pin.dart"; class LockScreenOptions extends StatefulWidget { const LockScreenOptions({super.key}); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart index 93a1c4d636..eb5f2b94ed 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_password.dart @@ -1,16 +1,16 @@ import "dart:convert"; +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:ente_lock_screen/lock_screen_settings.dart"; +import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; +import "package:ente_lock_screen/ui/lock_screen_options.dart"; import "package:ente_strings/ente_strings.dart"; import "package:ente_ui/components/buttons/dynamic_fab.dart"; import "package:ente_ui/components/buttons/icon_button_widget.dart"; import "package:ente_ui/components/text_input_widget.dart"; import "package:ente_ui/theme/ente_theme.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; import "package:flutter/material.dart"; import "package:flutter/services.dart"; -import "package:ente_lock_screen/lock_screen_settings.dart"; -import "package:ente_lock_screen/ui/lock_screen_confirm_password.dart"; -import "package:ente_lock_screen/ui/lock_screen_options.dart"; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. /// Set to true when the app requires the user to authenticate before allowing diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 2001cae468..603ebdd67d 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -1,17 +1,17 @@ import "dart:convert"; import "dart:io"; -import "package:ente_strings/ente_strings.dart"; -import "package:ente_ui/theme/colors.dart"; -import "package:ente_ui/theme/ente_theme.dart"; -import "package:ente_ui/theme/text_style.dart"; import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; import "package:ente_lock_screen/lock_screen_settings.dart"; import "package:ente_lock_screen/ui/custom_pin_keypad.dart"; import "package:ente_lock_screen/ui/lock_screen_confirm_pin.dart"; import "package:ente_lock_screen/ui/lock_screen_options.dart"; +import "package:ente_strings/ente_strings.dart"; +import "package:ente_ui/theme/colors.dart"; +import "package:ente_ui/theme/ente_theme.dart"; +import "package:ente_ui/theme/text_style.dart"; +import "package:flutter/material.dart"; +import "package:flutter/services.dart"; import 'package:pinput/pinput.dart'; /// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index e5906cfc6a..5f6f1529c1 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -8,8 +8,6 @@ environment: flutter: ">=1.17.0" dependencies: - flutter: - sdk: flutter ente_accounts: path: ../accounts ente_configuration: @@ -25,6 +23,8 @@ dependencies: path: ../ui ente_utils: path: ../utils + flutter: + sdk: flutter flutter_animate: ^4.1.0 flutter_local_authentication: git: @@ -38,8 +38,8 @@ dependencies: shared_preferences: ^2.5.3 dev_dependencies: + flutter_lints: ^5.0.0 flutter_test: sdk: flutter - flutter_lints: ^5.0.0 flutter: From 094c92c8b6bc1ef2658764b4a098f8efe625490b Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:21:53 +0530 Subject: [PATCH 094/164] Lint lock_screen From 24f48b505417305dc431fb3a49c61dee38f1af52 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:23:25 +0530 Subject: [PATCH 095/164] Lint logging From c33396ea60bfa47bb8bd9bfaca71fd2c00ddc6f2 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:25 +0530 Subject: [PATCH 096/164] Lint network From 86b54e2241e173c66987a8ec88445ee024653652 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:34 +0530 Subject: [PATCH 097/164] Lint strings From fb64c8aa4cc1882efa5fb90f050af166fa13af15 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:54 +0530 Subject: [PATCH 098/164] Lint ui From d2cfa374bd772d5127997f774cd48921d25eff2d Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:58 +0530 Subject: [PATCH 099/164] Lint utils --- mobile/packages/utils/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index cdce22ee00..4ec3dd3c90 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -10,8 +10,8 @@ environment: dependencies: archive: ^4.0.7 email_validator: ^3.0.0 - file_saver: ^0.3.0 - flutter_email_sender: ^7.0.0 + ente_configuration: + path: ../../packages/configuration ente_logging: path: ../../packages/logging ente_strings: From 5dd5756a417a296a8dd161ba2a4c498fb01ca98b Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 16:29:58 +0530 Subject: [PATCH 100/164] Lint utils From 97b36681dc24c3363e0f0f75e8c560afcb1a56e0 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 21 Jul 2025 17:15:17 +0530 Subject: [PATCH 101/164] Consistency --- .../lib/pages/delete_account_page.dart | 8 +++--- .../lib/pages/password_reentry_page.dart | 15 ++++++++--- .../accounts/lib/pages/recovery_page.dart | 4 +-- .../accounts/lib/services/user_service.dart | 25 +++++++++++++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/mobile/packages/accounts/lib/pages/delete_account_page.dart b/mobile/packages/accounts/lib/pages/delete_account_page.dart index 66963914d7..f79e32cac8 100644 --- a/mobile/packages/accounts/lib/pages/delete_account_page.dart +++ b/mobile/packages/accounts/lib/pages/delete_account_page.dart @@ -13,10 +13,10 @@ import 'package:ente_utils/platform_util.dart'; import 'package:flutter/material.dart'; class DeleteAccountPage extends StatelessWidget { - final BaseConfiguration configuration; + final BaseConfiguration config; const DeleteAccountPage( - this.configuration, { + this.config, { super.key, }); @@ -169,9 +169,9 @@ class DeleteAccountPage extends StatelessWidget { final decryptChallenge = CryptoUtil.openSealSync( CryptoUtil.base642bin(response.encryptedChallenge), CryptoUtil.base642bin( - configuration.getKeyAttributes()!.publicKey, + config.getKeyAttributes()!.publicKey, ), - configuration.getSecretKey()!, + config.getSecretKey()!, ); final challengeResponseStr = utf8.decode(decryptChallenge); await UserService.instance.deleteAccount(context, challengeResponseStr); diff --git a/mobile/packages/accounts/lib/pages/password_reentry_page.dart b/mobile/packages/accounts/lib/pages/password_reentry_page.dart index 7e081c9a5c..f684fc1379 100644 --- a/mobile/packages/accounts/lib/pages/password_reentry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_reentry_page.dart @@ -15,10 +15,14 @@ import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; class PasswordReentryPage extends StatefulWidget { - final BaseHomePage homePage; final BaseConfiguration config; + final BaseHomePage homePage; - const PasswordReentryPage(this.homePage, this.config, {super.key}); + const PasswordReentryPage( + this.config, + this.homePage, { + super.key, + }); @override State createState() => _PasswordReentryPageState(); @@ -122,7 +126,10 @@ class _PasswordReentryPageState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return RecoveryPage(widget.homePage, widget.config); + return RecoveryPage( + widget.config, + widget.homePage, + ); }, ), ); @@ -272,8 +279,8 @@ class _PasswordReentryPageState extends State { MaterialPageRoute( builder: (BuildContext context) { return RecoveryPage( - widget.homePage, widget.config, + widget.homePage, ); }, ), diff --git a/mobile/packages/accounts/lib/pages/recovery_page.dart b/mobile/packages/accounts/lib/pages/recovery_page.dart index cc889bc34c..93d22991bf 100644 --- a/mobile/packages/accounts/lib/pages/recovery_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_page.dart @@ -8,10 +8,10 @@ import 'package:ente_ui/utils/toast_util.dart'; import 'package:flutter/material.dart'; class RecoveryPage extends StatefulWidget { - final BaseHomePage homePage; final BaseConfiguration config; + final BaseHomePage homePage; - const RecoveryPage(this.homePage, this.config, {super.key}); + const RecoveryPage(this.config, this.homePage, {super.key}); @override State createState() => _RecoveryPageState(); diff --git a/mobile/packages/accounts/lib/services/user_service.dart b/mobile/packages/accounts/lib/services/user_service.dart index f127fe50be..7bc0a54c64 100644 --- a/mobile/packages/accounts/lib/services/user_service.dart +++ b/mobile/packages/accounts/lib/services/user_service.dart @@ -348,7 +348,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, @@ -426,9 +429,15 @@ class UserService { await _saveConfiguration(response); if (_config.getEncryptedToken() != null) { if (isResettingPasswordScreen) { - page = RecoveryPage(_homePage, _config); + page = RecoveryPage( + _config, + _homePage, + ); } else { - page = PasswordReentryPage(_homePage, _config); + page = PasswordReentryPage( + _config, + _homePage, + ); } } else { page = PasswordEntryPage( @@ -837,7 +846,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, @@ -1001,7 +1013,10 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return PasswordReentryPage(_homePage, _config); + return PasswordReentryPage( + _config, + _homePage, + ); }, ), (route) => route.isFirst, From 661e1f92d59889af24f2cd9cbd7f32731e8f8d57 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 22 Jul 2025 00:15:51 +0530 Subject: [PATCH 102/164] Add developer settings page From abd733934bd1b3128de9f256e694e6cdd4a7c16a Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 31 Jul 2025 19:44:40 +0530 Subject: [PATCH 103/164] Introduced lock screen package from packages/lockscreen --- mobile/apps/auth/lib/main.dart | 18 +- .../local_authentication_service.dart | 144 ------- .../lib/ui/account/delete_account_page.dart | 4 +- mobile/apps/auth/lib/ui/code_widget.dart | 2 +- mobile/apps/auth/lib/ui/home_page.dart | 4 +- .../ui/settings/account_section_widget.dart | 4 +- .../ui/settings/data/duplicate_code_page.dart | 4 +- .../lib/ui/settings/data/export_widget.dart | 4 +- .../lock_screen/custom_pin_keypad.dart | 235 ----------- .../lock_screen/lock_screen_auto_lock.dart | 143 ------- .../lock_screen_confirm_password.dart | 186 -------- .../lock_screen/lock_screen_confirm_pin.dart | 211 ---------- .../lock_screen/lock_screen_options.dart | 397 ------------------ .../lock_screen/lock_screen_password.dart | 250 ----------- .../settings/lock_screen/lock_screen_pin.dart | 284 ------------- .../ui/settings/security_section_widget.dart | 14 +- mobile/apps/auth/lib/ui/settings_page.dart | 4 +- mobile/apps/auth/lib/ui/tools/app_lock.dart | 209 --------- .../apps/auth/lib/ui/tools/lock_screen.dart | 367 ---------------- mobile/apps/auth/lib/utils/auth_util.dart | 66 --- .../auth/lib/utils/lock_screen_settings.dart | 253 ----------- mobile/apps/auth/pubspec.lock | 35 ++ mobile/apps/auth/pubspec.yaml | 2 + mobile/packages/ui/pubspec.lock | 77 ++-- mobile/packages/ui/pubspec.yaml | 6 +- 25 files changed, 117 insertions(+), 2806 deletions(-) delete mode 100644 mobile/apps/auth/lib/services/local_authentication_service.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/custom_pin_keypad.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_auto_lock.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_options.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_password.dart delete mode 100644 mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_pin.dart delete mode 100644 mobile/apps/auth/lib/ui/tools/app_lock.dart delete mode 100644 mobile/apps/auth/lib/ui/tools/lock_screen.dart delete mode 100644 mobile/apps/auth/lib/utils/auth_util.dart delete mode 100644 mobile/apps/auth/lib/utils/lock_screen_settings.dart diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 29c8279abe..94cdb778ca 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -6,6 +6,7 @@ import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/ente_theme_data.dart'; +import 'package:ente_auth/l10n/arb/app_localizations.dart'; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/billing_service.dart'; @@ -17,14 +18,14 @@ import 'package:ente_auth/services/window_listener_service.dart'; import 'package:ente_auth/store/authenticator_db.dart'; import 'package:ente_auth/store/code_display_store.dart'; import 'package:ente_auth/store/code_store.dart'; -import 'package:ente_auth/ui/tools/app_lock.dart'; -import 'package:ente_auth/ui/tools/lock_screen.dart'; import 'package:ente_auth/ui/utils/icon_utils.dart'; import 'package:ente_auth/utils/directory_utils.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/window_protocol_handler.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen.dart'; import 'package:ente_logging/logging.dart'; import 'package:ente_network/network.dart'; import 'package:flutter/foundation.dart'; @@ -88,6 +89,7 @@ void main() async { Future _runInForeground() async { final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode()); + final configuration = Configuration.instance; return await _runWithLogs(() async { _logger.info("Starting app in foreground"); try { @@ -101,12 +103,18 @@ Future _runInForeground() async { runApp( AppLock( builder: (args) => App(locale: locale), - lockScreen: const LockScreen(), + lockScreen: LockScreen(configuration), enabled: await LockScreenSettings.instance.shouldShowLockScreen(), locale: locale, lightTheme: lightThemeData, darkTheme: darkThemeData, savedThemeMode: savedThemeMode, + localeListResolutionCallback: localResolutionCallBack, + localizationsDelegates: const [ + ...AppLocalizations.localizationsDelegates, + ], + supportedLocales: appSupportedLocales, + backgroundLockLatency: const Duration(seconds: 0), ), ); }); @@ -160,5 +168,5 @@ Future _init(bool bool, {String? via}) async { await NotificationService.instance.init(); await UpdateService.instance.init(); await IconUtils.instance.init(); - await LockScreenSettings.instance.init(); + await LockScreenSettings.instance.init(Configuration.instance); } diff --git a/mobile/apps/auth/lib/services/local_authentication_service.dart b/mobile/apps/auth/lib/services/local_authentication_service.dart deleted file mode 100644 index 1502b2a36e..0000000000 --- a/mobile/apps/auth/lib/services/local_authentication_service.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'dart:io'; - -import 'package:ente_auth/ui/settings/lock_screen/lock_screen_password.dart'; -import 'package:ente_auth/ui/settings/lock_screen/lock_screen_pin.dart'; -import 'package:ente_auth/ui/tools/app_lock.dart'; -import 'package:ente_auth/utils/auth_util.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_local_authentication/flutter_local_authentication.dart'; -import 'package:local_auth/local_auth.dart'; -import 'package:logging/logging.dart'; - -class LocalAuthenticationService { - LocalAuthenticationService._privateConstructor(); - static final LocalAuthenticationService instance = - LocalAuthenticationService._privateConstructor(); - final logger = Logger((LocalAuthenticationService).toString()); - int lastAuthTime = 0; - - Future requestLocalAuthentication( - BuildContext context, - String infoMessage, - ) async { - if (kDebugMode) { - // if last auth time is less than 60 seconds, don't ask for auth again - if (lastAuthTime != 0 && - DateTime.now().millisecondsSinceEpoch - lastAuthTime < 60000) { - return true; - } - } - if (await isLocalAuthSupportedOnDevice() || - LockScreenSettings.instance.getIsAppLockSet()) { - AppLock.of(context)!.setEnabled(false); - final result = await requestAuthentication( - context, - infoMessage, - isAuthenticatingForInAppChange: true, - ); - AppLock.of(context)!.setEnabled( - await LockScreenSettings.instance.shouldShowLockScreen(), - ); - if (!result) { - showToast(context, infoMessage); - return false; - } else { - lastAuthTime = DateTime.now().millisecondsSinceEpoch; - return true; - } - } - return true; - } - - Future requestEnteAuthForLockScreen( - BuildContext context, - String? savedPin, - String? savedPassword, { - bool isAuthenticatingOnAppLaunch = false, - bool isAuthenticatingForInAppChange = false, - }) async { - if (savedPassword != null) { - final result = await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return LockScreenPassword( - isChangingLockScreenSettings: true, - isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, - isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch, - authPass: savedPassword, - ); - }, - ), - ); - if (result) { - return true; - } - } - if (savedPin != null) { - final result = await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return LockScreenPin( - isChangingLockScreenSettings: true, - isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, - isAuthenticatingOnAppLaunch: isAuthenticatingOnAppLaunch, - authPin: savedPin, - ); - }, - ), - ); - if (result) { - return true; - } - } - return false; - } - - Future requestLocalAuthForLockScreen( - BuildContext context, - bool shouldEnableLockScreen, - String infoMessage, - String errorDialogContent, [ - String errorDialogTitle = "", - ]) async { - if (await isLocalAuthSupportedOnDevice()) { - AppLock.of(context)!.disable(); - final result = await requestAuthentication( - context, - infoMessage, - ); - if (result) { - AppLock.of(context)!.setEnabled(shouldEnableLockScreen); - await LockScreenSettings.instance - .setSystemLockScreen(shouldEnableLockScreen); - return true; - } else { - AppLock.of(context)!.setEnabled( - await LockScreenSettings.instance.shouldShowLockScreen(), - ); - } - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - errorDialogTitle, - errorDialogContent, - ); - } - return false; - } - - Future isLocalAuthSupportedOnDevice() async { - try { - return Platform.isLinux - ? await FlutterLocalAuthentication().canAuthenticate() - : await LocalAuthentication().isDeviceSupported(); - } on MissingPluginException { - return false; - } - } -} diff --git a/mobile/apps/auth/lib/ui/account/delete_account_page.dart b/mobile/apps/auth/lib/ui/account/delete_account_page.dart index b8779e8cbb..d11b0ccaad 100644 --- a/mobile/apps/auth/lib/ui/account/delete_account_page.dart +++ b/mobile/apps/auth/lib/ui/account/delete_account_page.dart @@ -3,13 +3,13 @@ import 'dart:convert'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/delete_account.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; -import 'package:ente_auth/services/user_service.dart'; + import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/common/dialogs.dart'; import 'package:ente_auth/ui/common/gradient_button.dart'; import 'package:ente_auth/utils/email_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:flutter/material.dart'; class DeleteAccountPage extends StatelessWidget { diff --git a/mobile/apps/auth/lib/ui/code_widget.dart b/mobile/apps/auth/lib/ui/code_widget.dart index be9c2b8a69..df57d9b8a6 100644 --- a/mobile/apps/auth/lib/ui/code_widget.dart +++ b/mobile/apps/auth/lib/ui/code_widget.dart @@ -9,7 +9,6 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart'; import 'package:ente_auth/onboarding/view/view_qr_page.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/ente_theme.dart'; @@ -22,6 +21,7 @@ import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_context_menu/flutter_context_menu.dart'; diff --git a/mobile/apps/auth/lib/ui/home_page.dart b/mobile/apps/auth/lib/ui/home_page.dart index e218e4fbda..af1deec96f 100644 --- a/mobile/apps/auth/lib/ui/home_page.dart +++ b/mobile/apps/auth/lib/ui/home_page.dart @@ -33,12 +33,12 @@ import 'package:ente_auth/ui/reorder_codes_page.dart'; import 'package:ente_auth/ui/scanner_page.dart'; import 'package:ente_auth/ui/settings_page.dart'; import 'package:ente_auth/ui/sort_option_menu.dart'; -import 'package:ente_auth/ui/tools/app_lock.dart'; import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/totp_util.dart'; import 'package:ente_events/event_bus.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart index 47e639c2e7..89719c1da7 100644 --- a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart @@ -1,6 +1,5 @@ import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/account/change_email_dialog.dart'; @@ -15,6 +14,7 @@ import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:flutter/material.dart'; class AccountSectionWidget extends StatelessWidget { diff --git a/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart b/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart index 9de8695a52..0aba15ed34 100644 --- a/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart +++ b/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart @@ -1,11 +1,11 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; -import 'package:ente_auth/services/deduplication_service.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; +import 'package:ente_auth/services/deduplication_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/code_widget.dart'; import 'package:ente_auth/utils/dialog_util.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/data/export_widget.dart b/mobile/apps/auth/lib/ui/settings/data/export_widget.dart index 3ec41b2fc2..2ab7030874 100644 --- a/mobile/apps/auth/lib/ui/settings/data/export_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/data/export_widget.dart @@ -2,8 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/export/ente.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; +import 'package:ente_auth/models/export/ente.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/dialog_widget.dart'; @@ -14,6 +13,7 @@ import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/share_utils.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:file_saver/file_saver.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/custom_pin_keypad.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/custom_pin_keypad.dart deleted file mode 100644 index f8ad367fe0..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/custom_pin_keypad.dart +++ /dev/null @@ -1,235 +0,0 @@ -import "package:ente_auth/theme/ente_theme.dart"; -import "package:flutter/material.dart"; - -class CustomPinKeypad extends StatelessWidget { - final TextEditingController controller; - const CustomPinKeypad({required this.controller, super.key}); - - @override - Widget build(BuildContext context) { - return SafeArea( - child: Container( - padding: const EdgeInsets.all(2), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Container( - color: getEnteColorScheme(context).strokeFainter, - child: Column( - children: [ - Row( - children: [ - _Button( - text: '', - number: '1', - onTap: () { - _onKeyTap('1'); - }, - ), - _Button( - text: "ABC", - number: '2', - onTap: () { - _onKeyTap('2'); - }, - ), - _Button( - text: "DEF", - number: '3', - onTap: () { - _onKeyTap('3'); - }, - ), - ], - ), - Row( - children: [ - _Button( - number: '4', - text: "GHI", - onTap: () { - _onKeyTap('4'); - }, - ), - _Button( - number: '5', - text: 'JKL', - onTap: () { - _onKeyTap('5'); - }, - ), - _Button( - number: '6', - text: 'MNO', - onTap: () { - _onKeyTap('6'); - }, - ), - ], - ), - Row( - children: [ - _Button( - number: '7', - text: 'PQRS', - onTap: () { - _onKeyTap('7'); - }, - ), - _Button( - number: '8', - text: 'TUV', - onTap: () { - _onKeyTap('8'); - }, - ), - _Button( - number: '9', - text: 'WXYZ', - onTap: () { - _onKeyTap('9'); - }, - ), - ], - ), - Row( - children: [ - const _Button( - number: '', - text: '', - muteButton: true, - onTap: null, - ), - _Button( - number: '0', - text: '', - onTap: () { - _onKeyTap('0'); - }, - ), - _Button( - number: '', - text: '', - icon: const Icon(Icons.backspace_outlined), - onTap: () { - _onBackspace(); - }, - ), - ], - ), - ], - ), - ), - ], - ), - ), - ); - } - - void _onKeyTap(String number) { - controller.text += number; - return; - } - - void _onBackspace() { - if (controller.text.isNotEmpty) { - controller.text = - controller.text.substring(0, controller.text.length - 1); - } - return; - } -} - -class _Button extends StatefulWidget { - final String number; - final String text; - final VoidCallback? onTap; - final bool muteButton; - final Widget? icon; - - const _Button({ - required this.number, - required this.text, - this.muteButton = false, - required this.onTap, - this.icon, - }); - - @override - State<_Button> createState() => _ButtonState(); -} - -class _ButtonState extends State<_Button> { - bool isPressed = false; - - void _onTapDown(TapDownDetails details) { - setState(() { - isPressed = true; - }); - } - - void _onTapUp(TapUpDetails details) async { - setState(() { - isPressed = false; - }); - } - - @override - Widget build(BuildContext context) { - final colorScheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - return Expanded( - child: GestureDetector( - onTap: widget.onTap, - onTapDown: _onTapDown, - onTapUp: _onTapUp, - child: AnimatedContainer( - duration: const Duration(milliseconds: 100), - curve: Curves.easeOut, - child: Container( - margin: const EdgeInsets.all(4), - decoration: BoxDecoration( - shape: BoxShape.rectangle, - borderRadius: BorderRadius.circular(6), - color: isPressed - ? colorScheme.backgroundElevated - : widget.muteButton - ? colorScheme.fillFaintPressed - : widget.icon == null - ? colorScheme.backgroundElevated2 - : null, - ), - child: Center( - child: widget.muteButton - ? const SizedBox.shrink() - : widget.icon != null - ? Container( - padding: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 10, - ), - child: widget.icon, - ) - : Container( - padding: const EdgeInsets.all(4), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - widget.number, - style: textTheme.h3, - ), - Text( - widget.text, - style: textTheme.tinyBold, - ), - ], - ), - ), - ), - ), - ), - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_auto_lock.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_auto_lock.dart deleted file mode 100644 index 869bb1e40a..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_auto_lock.dart +++ /dev/null @@ -1,143 +0,0 @@ -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/components/captioned_text_widget.dart'; -import 'package:ente_auth/ui/components/divider_widget.dart'; -import 'package:ente_auth/ui/components/menu_item_widget.dart'; -import 'package:ente_auth/ui/components/separators.dart'; -import 'package:ente_auth/ui/components/title_bar_title_widget.dart'; -import 'package:ente_auth/ui/components/title_bar_widget.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; -import 'package:flutter/material.dart'; - -class LockScreenAutoLock extends StatefulWidget { - const LockScreenAutoLock({super.key}); - - @override - State createState() => _LockScreenAutoLockState(); -} - -class _LockScreenAutoLockState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: CustomScrollView( - primary: false, - slivers: [ - TitleBarWidget( - flexibleSpaceTitle: TitleBarTitleWidget( - title: context.l10n.autoLock, - ), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return const Padding( - padding: EdgeInsets.symmetric( - horizontal: 16, - vertical: 20, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Column( - children: [ - ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), - child: AutoLockItems(), - ), - ], - ), - ], - ), - ); - }, - childCount: 1, - ), - ), - ], - ), - ); - } -} - -class AutoLockItems extends StatefulWidget { - const AutoLockItems({super.key}); - - @override - State createState() => _AutoLockItemsState(); -} - -class _AutoLockItemsState extends State { - final autoLockDurations = LockScreenSettings.instance.autoLockDurations; - List items = []; - Duration currentAutoLockTime = const Duration(seconds: 5); - - @override - void initState() { - for (Duration autoLockDuration in autoLockDurations) { - if (autoLockDuration.inMilliseconds == - LockScreenSettings.instance.getAutoLockTime()) { - currentAutoLockTime = autoLockDuration; - break; - } - } - super.initState(); - } - - @override - Widget build(BuildContext context) { - items.clear(); - for (Duration autoLockDuration in autoLockDurations) { - items.add( - _menuItemForPicker(autoLockDuration), - ); - } - items = addSeparators( - items, - DividerWidget( - dividerType: DividerType.menuNoIcon, - bgColor: getEnteColorScheme(context).fillFaint, - ), - ); - return Column( - mainAxisSize: MainAxisSize.min, - children: items, - ); - } - - Widget _menuItemForPicker(Duration autoLockTime) { - return MenuItemWidget( - key: ValueKey(autoLockTime), - menuItemColor: getEnteColorScheme(context).fillFaint, - captionedTextWidget: CaptionedTextWidget( - title: _formatTime(autoLockTime), - ), - trailingIcon: currentAutoLockTime == autoLockTime ? Icons.check : null, - alignCaptionedTextToLeft: true, - isTopBorderRadiusRemoved: true, - isBottomBorderRadiusRemoved: true, - showOnlyLoadingState: true, - onTap: () async { - await LockScreenSettings.instance.setAutoLockTime(autoLockTime).then( - (value) => { - setState(() { - currentAutoLockTime = autoLockTime; - }), - }, - ); - }, - ); - } - - String _formatTime(Duration duration) { - if (duration.inHours != 0) { - return "${duration.inHours}hr"; - } else if (duration.inMinutes != 0) { - return "${duration.inMinutes}m"; - } else if (duration.inSeconds != 0) { - return "${duration.inSeconds}s"; - } else { - return context.l10n.immediately; - } - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart deleted file mode 100644 index dee6fd2db9..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_password.dart +++ /dev/null @@ -1,186 +0,0 @@ -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import "package:ente_auth/ui/common/dynamic_fab.dart"; -import "package:ente_auth/ui/components/buttons/icon_button_widget.dart"; -import "package:ente_auth/ui/components/text_input_widget.dart"; -import "package:ente_auth/utils/lock_screen_settings.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; - -class LockScreenConfirmPassword extends StatefulWidget { - const LockScreenConfirmPassword({ - super.key, - required this.password, - }); - final String password; - - @override - State createState() => - _LockScreenConfirmPasswordState(); -} - -class _LockScreenConfirmPasswordState extends State { - final _confirmPasswordController = TextEditingController(text: null); - final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; - final _focusNode = FocusNode(); - final _isFormValid = ValueNotifier(false); - final _submitNotifier = ValueNotifier(false); - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) async { - _focusNode.requestFocus(); - }); - } - - @override - void dispose() { - _submitNotifier.dispose(); - _focusNode.dispose(); - _isFormValid.dispose(); - _confirmPasswordController.dispose(); - super.dispose(); - } - - Future _confirmPasswordMatch() async { - if (widget.password == _confirmPasswordController.text) { - await _lockscreenSetting.setPassword(_confirmPasswordController.text); - - Navigator.of(context).pop(true); - Navigator.of(context).pop(true); - return; - } - await HapticFeedback.vibrate(); - throw Exception("Incorrect password"); - } - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - final isKeypadOpen = MediaQuery.viewInsetsOf(context).bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - onPressed: () { - FocusScope.of(context).unfocus(); - Navigator.of(context).pop(); - }, - icon: Icon( - Icons.arrow_back, - color: colorTheme.textBase, - ), - ), - ), - floatingActionButton: ValueListenableBuilder( - valueListenable: _isFormValid, - builder: (context, isFormValid, child) { - return DynamicFAB( - isKeypadOpen: isKeypadOpen, - buttonText: context.l10n.confirm, - isFormValid: isFormValid, - onPressedFunction: () async { - _submitNotifier.value = !_submitNotifier.value; - }, - ); - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - body: SingleChildScrollView( - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 120, - width: 120, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 82, - height: 82, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - Colors.grey.shade500.withOpacity(0.2), - Colors.grey.shade50.withOpacity(0.1), - Colors.grey.shade400.withOpacity(0.2), - Colors.grey.shade300.withOpacity(0.4), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorTheme.backgroundBase, - ), - ), - ), - ), - SizedBox( - height: 75, - width: 75, - child: CircularProgressIndicator( - color: colorTheme.fillFaintPressed, - value: 1, - strokeWidth: 1.5, - ), - ), - IconButtonWidget( - icon: Icons.lock, - iconButtonType: IconButtonType.primary, - iconColor: colorTheme.textBase, - ), - ], - ), - ), - Text( - context.l10n.reEnterPassword, - style: textTheme.bodyBold, - ), - const Padding(padding: EdgeInsets.all(12)), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: TextInputWidget( - hintText: context.l10n.confirmPassword, - autoFocus: true, - textCapitalization: TextCapitalization.none, - isPasswordInput: true, - shouldSurfaceExecutionStates: false, - onChange: (p0) { - _confirmPasswordController.text = p0; - _isFormValid.value = - _confirmPasswordController.text.isNotEmpty; - }, - onSubmit: (p0) { - return _confirmPasswordMatch(); - }, - submitNotifier: _submitNotifier, - ), - ), - const Padding(padding: EdgeInsets.all(12)), - ], - ), - ), - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart deleted file mode 100644 index 5ed7c48796..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_confirm_pin.dart +++ /dev/null @@ -1,211 +0,0 @@ -import "dart:io"; - -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import "package:ente_auth/ui/settings/lock_screen/custom_pin_keypad.dart"; -import "package:ente_auth/utils/lock_screen_settings.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; -import "package:pinput/pinput.dart"; - -class LockScreenConfirmPin extends StatefulWidget { - const LockScreenConfirmPin({super.key, required this.pin}); - final String pin; - @override - State createState() => _LockScreenConfirmPinState(); -} - -class _LockScreenConfirmPinState extends State { - final _confirmPinController = TextEditingController(text: null); - bool isConfirmPinValid = false; - bool isPlatformDesktop = false; - final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; - final _pinPutDecoration = PinTheme( - height: 48, - width: 48, - padding: const EdgeInsets.only(top: 6.0), - decoration: BoxDecoration( - border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)), - borderRadius: BorderRadius.circular(15.0), - ), - ); - - @override - void initState() { - super.initState(); - isPlatformDesktop = - Platform.isLinux || Platform.isMacOS || Platform.isWindows; - } - - @override - void dispose() { - super.dispose(); - _confirmPinController.dispose(); - } - - Future _confirmPinMatch() async { - if (widget.pin == _confirmPinController.text) { - await _lockscreenSetting.setPin(_confirmPinController.text); - - Navigator.of(context).pop(true); - Navigator.of(context).pop(true); - return; - } - setState(() { - isConfirmPinValid = true; - }); - await HapticFeedback.vibrate(); - await Future.delayed(const Duration(milliseconds: 75)); - _confirmPinController.clear(); - setState(() { - isConfirmPinValid = false; - }); - } - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - return Scaffold( - appBar: AppBar( - elevation: 0, - leading: IconButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - icon: Icon( - Icons.arrow_back, - color: colorTheme.textBase, - ), - ), - ), - floatingActionButton: isPlatformDesktop - ? null - : CustomPinKeypad(controller: _confirmPinController), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - body: SingleChildScrollView( - child: _getBody(colorTheme, textTheme), - ), - ); - } - - Widget _getBody(colorTheme, textTheme) { - return Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 120, - width: 120, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 82, - height: 82, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - Colors.grey.shade500.withOpacity(0.2), - Colors.grey.shade50.withOpacity(0.1), - Colors.grey.shade400.withOpacity(0.2), - Colors.grey.shade300.withOpacity(0.4), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorTheme.backgroundBase, - ), - ), - ), - ), - SizedBox( - height: 75, - width: 75, - child: ValueListenableBuilder( - valueListenable: _confirmPinController, - builder: (context, value, child) { - return TweenAnimationBuilder( - tween: Tween( - begin: 0, - end: _confirmPinController.text.length / 4, - ), - curve: Curves.ease, - duration: const Duration(milliseconds: 250), - builder: (context, value, _) => - CircularProgressIndicator( - backgroundColor: colorTheme.fillFaintPressed, - value: value, - color: colorTheme.primary400, - strokeWidth: 1.5, - ), - ); - }, - ), - ), - Icon( - Icons.lock, - color: colorTheme.textBase, - size: 30, - ), - ], - ), - ), - Text( - context.l10n.reEnterPin, - style: textTheme.bodyBold, - ), - const Padding(padding: EdgeInsets.all(12)), - Pinput( - length: 4, - showCursor: false, - useNativeKeyboard: isPlatformDesktop, - autofocus: true, - controller: _confirmPinController, - defaultPinTheme: _pinPutDecoration, - submittedPinTheme: _pinPutDecoration.copyWith( - textStyle: textTheme.h3Bold, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.fillBase, - ), - ), - ), - followingPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.fillMuted, - ), - ), - ), - focusedPinTheme: _pinPutDecoration, - errorPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.warning400, - ), - ), - ), - errorText: '', - obscureText: true, - obscuringCharacter: '*', - forceErrorState: isConfirmPinValid, - onCompleted: (value) async { - await _confirmPinMatch(); - }, - ), - ], - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_options.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_options.dart deleted file mode 100644 index 7f48bca84d..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_options.dart +++ /dev/null @@ -1,397 +0,0 @@ -import "dart:async"; -import "dart:io"; - -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/services/local_authentication_service.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import "package:ente_auth/ui/components/buttons/button_widget.dart"; -import "package:ente_auth/ui/components/captioned_text_widget.dart"; -import "package:ente_auth/ui/components/dialog_widget.dart"; -import "package:ente_auth/ui/components/divider_widget.dart"; -import "package:ente_auth/ui/components/menu_item_widget.dart"; -import "package:ente_auth/ui/components/models/button_type.dart"; -import "package:ente_auth/ui/components/title_bar_title_widget.dart"; -import "package:ente_auth/ui/components/title_bar_widget.dart"; -import "package:ente_auth/ui/components/toggle_switch_widget.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_auto_lock.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_password.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_pin.dart"; -import "package:ente_auth/ui/tools/app_lock.dart"; -import "package:ente_auth/utils/lock_screen_settings.dart"; -import "package:ente_auth/utils/navigation_util.dart"; -import "package:ente_auth/utils/platform_util.dart"; -import "package:flutter/material.dart"; - -class LockScreenOptions extends StatefulWidget { - const LockScreenOptions({super.key}); - - @override - State createState() => _LockScreenOptionsState(); -} - -class _LockScreenOptionsState extends State { - final LockScreenSettings _lockScreenSettings = LockScreenSettings.instance; - late bool appLock = false; - bool isPinEnabled = false; - bool isPasswordEnabled = false; - late int autoLockTimeInMilliseconds; - late bool hideAppContent; - late bool isSystemLockEnabled = false; - - @override - void initState() { - super.initState(); - hideAppContent = _lockScreenSettings.getShouldHideAppContent(); - autoLockTimeInMilliseconds = _lockScreenSettings.getAutoLockTime(); - _initializeSettings(); - appLock = _lockScreenSettings.getIsAppLockSet(); - } - - Future _initializeSettings() async { - final bool passwordEnabled = await _lockScreenSettings.isPasswordSet(); - final bool pinEnabled = await _lockScreenSettings.isPinSet(); - final bool shouldHideAppContent = - _lockScreenSettings.getShouldHideAppContent(); - final bool systemLockEnabled = _lockScreenSettings.shouldShowSystemLockScreen(); - setState(() { - isPasswordEnabled = passwordEnabled; - isPinEnabled = pinEnabled; - hideAppContent = shouldHideAppContent; - isSystemLockEnabled = systemLockEnabled; - }); - } - - Future _deviceLock() async { - if (await LocalAuthenticationService.instance - .isLocalAuthSupportedOnDevice()) { - await _lockScreenSettings.removePinAndPassword(); - await _lockScreenSettings.setSystemLockScreen(!isSystemLockEnabled); - } else { - await showDialogWidget( - context: context, - title: context.l10n.noSystemLockFound, - body: context.l10n.deviceLockEnablePreSteps, - isDismissible: true, - buttons: const [ - ButtonWidget( - buttonType: ButtonType.secondary, - labelText: "OK", - isInAlert: true, - ), - ], - ); - } - await _initializeSettings(); - } - - Future _pinLock() async { - final bool result = await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return const LockScreenPin(); - }, - ), - ); - - if (result) { - await _lockScreenSettings.setSystemLockScreen(false); - await _lockScreenSettings.setAppLockEnabled(true); - setState(() { - appLock = _lockScreenSettings.getIsAppLockSet(); - }); - } - await _initializeSettings(); - } - - Future _passwordLock() async { - final bool result = await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return const LockScreenPassword(); - }, - ), - ); - if (result) { - await _lockScreenSettings.setSystemLockScreen(false); - setState(() { - appLock = _lockScreenSettings.getIsAppLockSet(); - }); - } - await _initializeSettings(); - } - - Future _onToggleSwitch() async { - AppLock.of(context)!.setEnabled(!appLock); - if (await LocalAuthenticationService.instance - .isLocalAuthSupportedOnDevice()) { - await _lockScreenSettings.setSystemLockScreen(!appLock); - await _lockScreenSettings.setAppLockEnabled(!appLock); - } else { - await _lockScreenSettings.setSystemLockScreen(false); - await _lockScreenSettings.setAppLockEnabled(false); - } - await _lockScreenSettings.removePinAndPassword(); - if (PlatformUtil.isMobile()) { - await _lockScreenSettings.setHideAppContent(!appLock); - setState(() { - hideAppContent = _lockScreenSettings.getShouldHideAppContent(); - }); - } - await _initializeSettings(); - setState(() { - appLock = !appLock; - }); - } - - Future _onAutoLock() async { - await routeToPage( - context, - const LockScreenAutoLock(), - ).then( - (value) { - setState(() { - autoLockTimeInMilliseconds = _lockScreenSettings.getAutoLockTime(); - }); - }, - ); - } - - Future _onHideContent() async { - setState(() { - hideAppContent = !hideAppContent; - }); - await _lockScreenSettings.setHideAppContent(hideAppContent); - } - - String _formatTime(Duration duration) { - if (duration.inHours != 0) { - return "in ${duration.inHours} hour${duration.inHours > 1 ? 's' : ''}"; - } else if (duration.inMinutes != 0) { - return "in ${duration.inMinutes} minute${duration.inMinutes > 1 ? 's' : ''}"; - } else if (duration.inSeconds != 0) { - return "in ${duration.inSeconds} second${duration.inSeconds > 1 ? 's' : ''}"; - } else { - return context.l10n.immediately; - } - } - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - return Scaffold( - body: CustomScrollView( - primary: false, - slivers: [ - TitleBarWidget( - flexibleSpaceTitle: TitleBarTitleWidget( - title: context.l10n.appLock, - ), - ), - SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: context.l10n.appLock, - ), - alignCaptionedTextToLeft: true, - singleBorderRadius: 8, - menuItemColor: colorTheme.fillFaint, - trailingWidget: ToggleSwitchWidget( - value: () => appLock, - onChanged: () => _onToggleSwitch(), - ), - ), - AnimatedSwitcher( - duration: const Duration(milliseconds: 210), - switchInCurve: Curves.easeOut, - switchOutCurve: Curves.easeIn, - child: !appLock - ? Padding( - padding: const EdgeInsets.only( - top: 14, - left: 14, - right: 12, - ), - child: Text( - context.l10n.appLockDescription, - style: textTheme.miniFaint, - textAlign: TextAlign.left, - ), - ) - : const SizedBox(), - ), - const Padding( - padding: EdgeInsets.only(top: 24), - ), - ], - ), - AnimatedSwitcher( - duration: const Duration(milliseconds: 210), - switchInCurve: Curves.easeOut, - switchOutCurve: Curves.easeIn, - child: appLock - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: context.l10n.deviceLock, - ), - surfaceExecutionStates: false, - alignCaptionedTextToLeft: true, - isTopBorderRadiusRemoved: false, - isBottomBorderRadiusRemoved: true, - menuItemColor: colorTheme.fillFaint, - trailingIcon: isSystemLockEnabled - ? Icons.check - : null, - trailingIconColor: colorTheme.textBase, - onTap: () => _deviceLock(), - ), - DividerWidget( - dividerType: DividerType.menuNoIcon, - bgColor: colorTheme.fillFaint, - ), - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: context.l10n.pinLock, - ), - surfaceExecutionStates: false, - alignCaptionedTextToLeft: true, - isTopBorderRadiusRemoved: true, - isBottomBorderRadiusRemoved: true, - menuItemColor: colorTheme.fillFaint, - trailingIcon: - isPinEnabled ? Icons.check : null, - trailingIconColor: colorTheme.textBase, - onTap: () => _pinLock(), - ), - DividerWidget( - dividerType: DividerType.menuNoIcon, - bgColor: colorTheme.fillFaint, - ), - MenuItemWidget( - captionedTextWidget: CaptionedTextWidget( - title: context.l10n.password, - ), - surfaceExecutionStates: false, - alignCaptionedTextToLeft: true, - isTopBorderRadiusRemoved: true, - isBottomBorderRadiusRemoved: false, - menuItemColor: colorTheme.fillFaint, - trailingIcon: isPasswordEnabled - ? Icons.check - : null, - trailingIconColor: colorTheme.textBase, - onTap: () => _passwordLock(), - ), - const SizedBox( - height: 24, - ), - PlatformUtil.isMobile() - ? MenuItemWidget( - captionedTextWidget: - CaptionedTextWidget( - title: context.l10n.autoLock, - subTitle: _formatTime( - Duration( - milliseconds: - autoLockTimeInMilliseconds, - ), - ), - ), - surfaceExecutionStates: false, - alignCaptionedTextToLeft: true, - singleBorderRadius: 8, - menuItemColor: colorTheme.fillFaint, - trailingIconColor: - colorTheme.textBase, - onTap: () => _onAutoLock(), - ) - : const SizedBox.shrink(), - PlatformUtil.isMobile() - ? Padding( - padding: const EdgeInsets.only( - top: 14, - left: 14, - right: 12, - ), - child: Text( - context.l10n - .autoLockFeatureDescription, - style: textTheme.miniFaint, - textAlign: TextAlign.left, - ), - ) - : const SizedBox.shrink(), - PlatformUtil.isMobile() - ? Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const SizedBox(height: 24), - MenuItemWidget( - captionedTextWidget: - CaptionedTextWidget( - title: - context.l10n.hideContent, - ), - alignCaptionedTextToLeft: true, - singleBorderRadius: 8, - menuItemColor: - colorTheme.fillFaint, - trailingWidget: - ToggleSwitchWidget( - value: () => hideAppContent, - onChanged: () => - _onHideContent(), - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 14, - left: 14, - right: 12, - ), - child: Text( - Platform.isAndroid - ? context.l10n - .hideContentDescriptionAndroid - : context.l10n - .hideContentDescriptioniOS, - style: textTheme.miniFaint, - textAlign: TextAlign.left, - ), - ), - ], - ) - : const SizedBox.shrink(), - ], - ) - : const SizedBox.shrink(), - ), - ], - ), - ), - ); - }, - childCount: 1, - ), - ), - ], - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_password.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_password.dart deleted file mode 100644 index 43a103523a..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_password.dart +++ /dev/null @@ -1,250 +0,0 @@ -import "dart:convert"; - -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import "package:ente_auth/ui/common/dynamic_fab.dart"; -import "package:ente_auth/ui/components/buttons/icon_button_widget.dart"; -import "package:ente_auth/ui/components/text_input_widget.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_confirm_password.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_options.dart"; -import "package:ente_auth/utils/lock_screen_settings.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; - -/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. -/// Set to true when the app requires the user to authenticate before allowing -/// changes to the lock screen settings. - -/// [isAuthenticatingOnAppLaunch] Authentication required on app launch. -/// Set to true when the app requires the user to authenticate immediately upon opening. - -/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password). -/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes. - -class LockScreenPassword extends StatefulWidget { - const LockScreenPassword({ - super.key, - this.isChangingLockScreenSettings = false, - this.isAuthenticatingOnAppLaunch = false, - this.isAuthenticatingForInAppChange = false, - this.authPass, - }); - - final bool isChangingLockScreenSettings; - final bool isAuthenticatingOnAppLaunch; - final bool isAuthenticatingForInAppChange; - final String? authPass; - @override - State createState() => _LockScreenPasswordState(); -} - -class _LockScreenPasswordState extends State { - final _passwordController = TextEditingController(text: null); - final _focusNode = FocusNode(); - final _isFormValid = ValueNotifier(false); - final _submitNotifier = ValueNotifier(false); - int invalidAttemptsCount = 0; - - final _lockscreenSetting = LockScreenSettings.instance; - @override - void initState() { - super.initState(); - invalidAttemptsCount = _lockscreenSetting.getInvalidAttemptCount(); - WidgetsBinding.instance.addPostFrameCallback((_) async { - _focusNode.requestFocus(); - }); - } - - @override - void dispose() { - super.dispose(); - _submitNotifier.dispose(); - _focusNode.dispose(); - _isFormValid.dispose(); - _passwordController.dispose(); - } - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - final isKeypadOpen = MediaQuery.viewInsetsOf(context).bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - onPressed: () { - FocusScope.of(context).unfocus(); - Navigator.of(context).pop(false); - }, - icon: Icon( - Icons.arrow_back, - color: colorTheme.textBase, - ), - ), - ), - floatingActionButton: ValueListenableBuilder( - valueListenable: _isFormValid, - builder: (context, isFormValid, child) { - return DynamicFAB( - isKeypadOpen: isKeypadOpen, - buttonText: context.l10n.next, - isFormValid: isFormValid, - onPressedFunction: () async { - _submitNotifier.value = !_submitNotifier.value; - }, - ); - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - body: SingleChildScrollView( - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 120, - width: 120, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 82, - height: 82, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - Colors.grey.shade500.withOpacity(0.2), - Colors.grey.shade50.withOpacity(0.1), - Colors.grey.shade400.withOpacity(0.2), - Colors.grey.shade300.withOpacity(0.4), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorTheme.backgroundBase, - ), - ), - ), - ), - SizedBox( - height: 75, - width: 75, - child: CircularProgressIndicator( - color: colorTheme.fillFaintPressed, - value: 1, - strokeWidth: 1.5, - ), - ), - IconButtonWidget( - icon: Icons.lock, - iconButtonType: IconButtonType.primary, - iconColor: colorTheme.textBase, - ), - ], - ), - ), - Text( - widget.isChangingLockScreenSettings - ? context.l10n.enterPassword - : context.l10n.setNewPassword, - textAlign: TextAlign.center, - style: textTheme.bodyBold, - ), - const Padding(padding: EdgeInsets.all(12)), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16), - child: TextInputWidget( - hintText: context.l10n.password, - autoFocus: true, - textCapitalization: TextCapitalization.none, - isPasswordInput: true, - shouldSurfaceExecutionStates: false, - onChange: (p0) { - _passwordController.text = p0; - _isFormValid.value = _passwordController.text.isNotEmpty; - }, - onSubmit: (p0) { - return _confirmPassword(); - }, - submitNotifier: _submitNotifier, - ), - ), - const Padding(padding: EdgeInsets.all(12)), - ], - ), - ), - ), - ); - } - - Future _confirmPasswordAuth(String inputtedPassword) async { - final Uint8List? salt = await _lockscreenSetting.getSalt(); - final hash = cryptoPwHash( - utf8.encode(inputtedPassword), - salt!, - sodium.crypto.pwhash.memLimitInteractive, - sodium.crypto.pwhash.opsLimitSensitive, - sodium, - ); - if (widget.authPass == base64Encode(hash)) { - await _lockscreenSetting.setInvalidAttemptCount(0); - - widget.isAuthenticatingOnAppLaunch || - widget.isAuthenticatingForInAppChange - ? Navigator.of(context).pop(true) - : Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LockScreenOptions(), - ), - ); - return true; - } else { - if (widget.isAuthenticatingOnAppLaunch) { - invalidAttemptsCount++; - await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); - if (invalidAttemptsCount > 4) { - Navigator.of(context).pop(false); - } - } - - await HapticFeedback.vibrate(); - throw Exception("Incorrect password"); - } - } - - Future _confirmPassword() async { - if (widget.isChangingLockScreenSettings) { - await _confirmPasswordAuth(_passwordController.text); - return; - } else { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) => LockScreenConfirmPassword( - password: _passwordController.text, - ), - ), - ); - _passwordController.clear(); - } - } -} diff --git a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_pin.dart b/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_pin.dart deleted file mode 100644 index 8cd4509f51..0000000000 --- a/mobile/apps/auth/lib/ui/settings/lock_screen/lock_screen_pin.dart +++ /dev/null @@ -1,284 +0,0 @@ -import "dart:convert"; -import "dart:io"; - -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/theme/colors.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import "package:ente_auth/theme/text_style.dart"; -import "package:ente_auth/ui/settings/lock_screen/custom_pin_keypad.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_confirm_pin.dart"; -import "package:ente_auth/ui/settings/lock_screen/lock_screen_options.dart"; -import "package:ente_auth/utils/lock_screen_settings.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:flutter/material.dart"; -import "package:flutter/services.dart"; -import 'package:pinput/pinput.dart'; - -/// [isChangingLockScreenSettings] Authentication required for changing lock screen settings. -/// Set to true when the app requires the user to authenticate before allowing -/// changes to the lock screen settings. - -/// [isAuthenticatingOnAppLaunch] Authentication required on app launch. -/// Set to true when the app requires the user to authenticate immediately upon opening. - -/// [isAuthenticatingForInAppChange] Authentication required for in-app changes (e.g., email, password). -/// Set to true when the app requires the to authenticate for sensitive actions like email, password changes. - -class LockScreenPin extends StatefulWidget { - const LockScreenPin({ - super.key, - this.isChangingLockScreenSettings = false, - this.isAuthenticatingOnAppLaunch = false, - this.isAuthenticatingForInAppChange = false, - this.authPin, - }); - - final bool isAuthenticatingOnAppLaunch; - final bool isChangingLockScreenSettings; - final bool isAuthenticatingForInAppChange; - final String? authPin; - @override - State createState() => _LockScreenPinState(); -} - -class _LockScreenPinState extends State { - final _pinController = TextEditingController(text: null); - - final LockScreenSettings _lockscreenSetting = LockScreenSettings.instance; - bool isPinValid = false; - int invalidAttemptsCount = 0; - bool isPlatformDesktop = false; - @override - void initState() { - super.initState(); - isPlatformDesktop = - Platform.isLinux || Platform.isMacOS || Platform.isWindows; - invalidAttemptsCount = _lockscreenSetting.getInvalidAttemptCount(); - } - - @override - void dispose() { - super.dispose(); - _pinController.dispose(); - } - - Future confirmPinAuth(String inputtedPin) async { - final Uint8List? salt = await _lockscreenSetting.getSalt(); - final hash = cryptoPwHash( - utf8.encode(inputtedPin), - salt!, - sodium.crypto.pwhash.memLimitInteractive, - sodium.crypto.pwhash.opsLimitSensitive, - sodium, - ); - if (widget.authPin == base64Encode(hash)) { - invalidAttemptsCount = 0; - await _lockscreenSetting.setInvalidAttemptCount(0); - widget.isAuthenticatingOnAppLaunch || - widget.isAuthenticatingForInAppChange - ? Navigator.of(context).pop(true) - : Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (context) => const LockScreenOptions(), - ), - ); - return true; - } else { - setState(() { - isPinValid = true; - }); - await HapticFeedback.vibrate(); - await Future.delayed(const Duration(milliseconds: 75)); - _pinController.clear(); - setState(() { - isPinValid = false; - }); - - if (widget.isAuthenticatingOnAppLaunch) { - invalidAttemptsCount++; - await _lockscreenSetting.setInvalidAttemptCount(invalidAttemptsCount); - if (invalidAttemptsCount > 4) { - Navigator.of(context).pop(false); - } - } - return false; - } - } - - Future _confirmPin(String inputtedPin) async { - if (widget.isChangingLockScreenSettings) { - await confirmPinAuth(inputtedPin); - return; - } else { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) => - LockScreenConfirmPin(pin: inputtedPin), - ), - ); - _pinController.clear(); - } - } - - final _pinPutDecoration = PinTheme( - height: 48, - width: 48, - padding: const EdgeInsets.only(top: 6.0), - decoration: BoxDecoration( - border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)), - borderRadius: BorderRadius.circular(15.0), - ), - ); - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - return Scaffold( - appBar: AppBar( - elevation: 0, - leading: IconButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - icon: Icon( - Icons.arrow_back, - color: colorTheme.textBase, - ), - ), - ), - floatingActionButton: isPlatformDesktop - ? null - : CustomPinKeypad(controller: _pinController), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - body: SingleChildScrollView( - child: _getBody(colorTheme, textTheme), - ), - ); - } - - Widget _getBody( - EnteColorScheme colorTheme, - EnteTextTheme textTheme, - ) { - return Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - height: 120, - width: 120, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 82, - height: 82, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - Colors.grey.shade500.withOpacity(0.2), - Colors.grey.shade50.withOpacity(0.1), - Colors.grey.shade400.withOpacity(0.2), - Colors.grey.shade300.withOpacity(0.4), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorTheme.backgroundBase, - ), - ), - ), - ), - SizedBox( - height: 75, - width: 75, - child: ValueListenableBuilder( - valueListenable: _pinController, - builder: (context, value, child) { - return TweenAnimationBuilder( - tween: Tween( - begin: 0, - end: _pinController.text.length / 4, - ), - curve: Curves.ease, - duration: const Duration(milliseconds: 250), - builder: (context, value, _) => - CircularProgressIndicator( - backgroundColor: colorTheme.fillFaintPressed, - value: value, - color: colorTheme.primary400, - strokeWidth: 1.5, - ), - ); - }, - ), - ), - Icon( - Icons.lock, - color: colorTheme.textBase, - size: 30, - ), - ], - ), - ), - Text( - widget.isChangingLockScreenSettings - ? context.l10n.enterPin - : context.l10n.setNewPin, - style: textTheme.bodyBold, - ), - const Padding(padding: EdgeInsets.all(12)), - Pinput( - length: 4, - showCursor: false, - useNativeKeyboard: isPlatformDesktop, - controller: _pinController, - autofocus: true, - defaultPinTheme: _pinPutDecoration, - submittedPinTheme: _pinPutDecoration.copyWith( - textStyle: textTheme.h3Bold, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.fillBase, - ), - ), - ), - followingPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.fillMuted, - ), - ), - ), - focusedPinTheme: _pinPutDecoration, - errorPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: colorTheme.warning400, - ), - ), - ), - forceErrorState: isPinValid, - obscureText: true, - obscuringCharacter: '*', - errorText: '', - onCompleted: (value) async { - await _confirmPin(_pinController.text); - }, - ), - ], - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart index dab16cb56f..1053e68b41 100644 --- a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart @@ -3,8 +3,7 @@ import 'dart:typed_data'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/user_details.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; +import 'package:ente_auth/models/user_details.dart'; import 'package:ente_auth/services/passkey_service.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; @@ -16,15 +15,16 @@ import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; import 'package:ente_auth/ui/components/menu_item_widget.dart'; import 'package:ente_auth/ui/components/models/button_result.dart'; import 'package:ente_auth/ui/components/toggle_switch_widget.dart'; -import 'package:ente_auth/ui/settings/common_settings.dart'; -import 'package:ente_auth/ui/settings/lock_screen/lock_screen_options.dart'; -import 'package:ente_auth/utils/auth_util.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; +import 'package:ente_auth/ui/settings/common_settings.dart'; +import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/auth_util.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/lock_screen_options.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/ui/settings_page.dart b/mobile/apps/auth/lib/ui/settings_page.dart index eb6ffca0fe..d543985b57 100644 --- a/mobile/apps/auth/lib/ui/settings_page.dart +++ b/mobile/apps/auth/lib/ui/settings_page.dart @@ -2,8 +2,7 @@ import 'dart:io'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/onboarding/view/onboarding_page.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; +import 'package:ente_auth/onboarding/view/onboarding_page.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/colors.dart'; @@ -27,6 +26,7 @@ import 'package:ente_auth/ui/settings/title_bar_widget.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; +import 'package:ente_lock_screen/local_authentication_service.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/apps/auth/lib/ui/tools/app_lock.dart b/mobile/apps/auth/lib/ui/tools/app_lock.dart deleted file mode 100644 index 1428c553c6..0000000000 --- a/mobile/apps/auth/lib/ui/tools/app_lock.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'dart:async'; - -import 'package:ente_auth/locale.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - -/// A widget which handles app lifecycle events for showing and hiding a lock screen. -/// This should wrap around a `MyApp` widget (or equivalent). -/// -/// [lockScreen] is a [Widget] which should be a screen for handling login logic and -/// calling `AppLock.of(context).didUnlock();` upon a successful login. -/// -/// [builder] is a [Function] taking an [Object] as its argument and should return a -/// [Widget]. The [Object] argument is provided by the [lockScreen] calling -/// `AppLock.of(context).didUnlock();` with an argument. [Object] can then be injected -/// in to your `MyApp` widget (or equivalent). -/// -/// [enabled] determines wether or not the [lockScreen] should be shown on app launch -/// and subsequent app pauses. This can be changed later on using `AppLock.of(context).enable();`, -/// `AppLock.of(context).disable();` or the convenience method `AppLock.of(context).setEnabled(enabled);` -/// using a bool argument. -/// -/// [backgroundLockLatency] determines how much time is allowed to pass when -/// the app is in the background state before the [lockScreen] widget should be -/// shown upon returning. It defaults to instantly. -/// - -// ignore_for_file: unnecessary_this, library_private_types_in_public_api -class AppLock extends StatefulWidget { - final Widget Function(Object?) builder; - final Widget lockScreen; - final bool enabled; - final Duration backgroundLockLatency; - final ThemeData? darkTheme; - final ThemeData? lightTheme; - final ThemeMode savedThemeMode; - final Locale? locale; - - const AppLock({ - super.key, - required this.builder, - required this.lockScreen, - required this.savedThemeMode, - this.enabled = true, - this.locale, - this.backgroundLockLatency = const Duration(seconds: 0), - this.darkTheme, - this.lightTheme, - }); - - static _AppLockState? of(BuildContext context) => - context.findAncestorStateOfType<_AppLockState>(); - - @override - State createState() => _AppLockState(); -} - -class _AppLockState extends State with WidgetsBindingObserver { - static final GlobalKey _navigatorKey = GlobalKey(); - - late bool _didUnlockForAppLaunch; - late bool _isLocked; - late bool _enabled; - - Timer? _backgroundLockLatencyTimer; - - @override - void initState() { - super.initState(); - - WidgetsBinding.instance.addObserver(this); - - this._didUnlockForAppLaunch = !this.widget.enabled; - this._isLocked = false; - this._enabled = this.widget.enabled; - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (!this._enabled) { - return; - } - - if (state == AppLifecycleState.paused && - (!this._isLocked && this._didUnlockForAppLaunch)) { - this._backgroundLockLatencyTimer = Timer( - Duration( - milliseconds: LockScreenSettings.instance.getAutoLockTime(), - ), - () => this.showLockScreen(), - ); - } - - if (state == AppLifecycleState.resumed) { - this._backgroundLockLatencyTimer?.cancel(); - } - - super.didChangeAppLifecycleState(state); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - - this._backgroundLockLatencyTimer?.cancel(); - - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return MaterialApp( - home: this.widget.enabled ? this._lockScreen : this.widget.builder(null), - navigatorKey: _navigatorKey, - themeMode: widget.savedThemeMode, - theme: widget.lightTheme, - darkTheme: widget.darkTheme, - locale: widget.locale, - supportedLocales: appSupportedLocales, - localeListResolutionCallback: localResolutionCallBack, - localizationsDelegates: const [ - ...AppLocalizations.localizationsDelegates, - ], - onGenerateRoute: (settings) { - switch (settings.name) { - case '/lock-screen': - return PageRouteBuilder( - pageBuilder: (_, __, ___) => this._lockScreen, - ); - case '/unlocked': - return PageRouteBuilder( - pageBuilder: (_, __, ___) => - this.widget.builder(settings.arguments), - ); - } - return PageRouteBuilder(pageBuilder: (_, __, ___) => this._lockScreen); - }, - ); - } - - Widget get _lockScreen { - return PopScope( - child: this.widget.lockScreen, - canPop: false, - ); - } - - /// Causes `AppLock` to either pop the [lockScreen] if the app is already running - /// or instantiates widget returned from the [builder] method if the app is cold - /// launched. - /// - /// [args] is an optional argument which will get passed to the [builder] method - /// when built. Use this when you want to inject objects created from the - /// [lockScreen] in to the rest of your app so you can better guarantee that some - /// objects, services or databases are already instantiated before using them. - void didUnlock([Object? args]) { - if (this._didUnlockForAppLaunch) { - this._didUnlockOnAppPaused(); - } else { - this._didUnlockOnAppLaunch(args); - } - } - - /// Makes sure that [AppLock] shows the [lockScreen] on subsequent app pauses if - /// [enabled] is true of makes sure it isn't shown on subsequent app pauses if - /// [enabled] is false. - /// - /// This is a convenience method for calling the [enable] or [disable] method based - /// on [enabled]. - void setEnabled(bool enabled) { - if (enabled) { - this.enable(); - } else { - this.disable(); - } - } - - /// Makes sure that [AppLock] shows the [lockScreen] on subsequent app pauses. - void enable() { - setState(() { - this._enabled = true; - }); - } - - /// Makes sure that [AppLock] doesn't show the [lockScreen] on subsequent app pauses. - void disable() { - setState(() { - this._enabled = false; - }); - } - - /// Manually show the [lockScreen]. - Future showLockScreen() { - this._isLocked = true; - return _navigatorKey.currentState!.pushNamed('/lock-screen'); - } - - void _didUnlockOnAppLaunch(Object? args) { - this._didUnlockForAppLaunch = true; - _navigatorKey.currentState! - .pushReplacementNamed('/unlocked', arguments: args); - } - - void _didUnlockOnAppPaused() { - this._isLocked = false; - _navigatorKey.currentState!.pop(); - } -} diff --git a/mobile/apps/auth/lib/ui/tools/lock_screen.dart b/mobile/apps/auth/lib/ui/tools/lock_screen.dart deleted file mode 100644 index 71adcd3f28..0000000000 --- a/mobile/apps/auth/lib/ui/tools/lock_screen.dart +++ /dev/null @@ -1,367 +0,0 @@ -import 'dart:io'; -import 'dart:math'; - -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/tools/app_lock.dart'; -import 'package:ente_auth/utils/auth_util.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:flutter_animate/flutter_animate.dart'; -import 'package:logging/logging.dart'; - -class LockScreen extends StatefulWidget { - const LockScreen({super.key}); - - @override - State createState() => _LockScreenState(); -} - -class _LockScreenState extends State with WidgetsBindingObserver { - final _logger = Logger("LockScreen"); - bool _isShowingLockScreen = false; - bool _hasPlacedAppInBackground = false; - bool _hasAuthenticationFailed = false; - int? lastAuthenticatingTime; - bool isTimerRunning = false; - int lockedTimeInSeconds = 0; - int invalidAttemptCount = 0; - int remainingTimeInSeconds = 0; - final _lockscreenSetting = LockScreenSettings.instance; - late Brightness _platformBrightness; - final bool isLoggedIn = Configuration.instance.isLoggedIn(); - - @override - void initState() { - _logger.info("initiatingState"); - super.initState(); - invalidAttemptCount = _lockscreenSetting.getInvalidAttemptCount(); - WidgetsBinding.instance.addObserver(this); - WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _showLockScreen(source: "postFrameInit"); - }); - _platformBrightness = - SchedulerBinding.instance.platformDispatcher.platformBrightness; - } - - @override - Widget build(BuildContext context) { - final colorTheme = getEnteColorScheme(context); - final textTheme = getEnteTextTheme(context); - return Scaffold( - appBar: AppBar( - elevation: 0, - leading: isLoggedIn - ? IconButton( - icon: const Icon(Icons.logout_outlined), - color: Theme.of(context).iconTheme.color, - onPressed: () { - _onLogoutTapped(context); - }, - ) - : const SizedBox.shrink(), - ), - body: GestureDetector( - onTap: () { - isTimerRunning ? null : _showLockScreen(source: "tap"); - }, - child: Container( - decoration: BoxDecoration( - image: DecorationImage( - opacity: _platformBrightness == Brightness.light ? 0.08 : 0.12, - image: const ExactAssetImage( - 'assets/loading_photos_background.png', - ), - fit: BoxFit.cover, - ), - ), - child: Center( - child: Column( - children: [ - const Spacer(), - SizedBox( - height: 120, - width: 120, - child: Stack( - alignment: Alignment.center, - children: [ - Container( - width: 82, - height: 82, - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - Colors.grey.shade500.withOpacity(0.2), - Colors.grey.shade50.withOpacity(0.1), - Colors.grey.shade400.withOpacity(0.2), - Colors.grey.shade300.withOpacity(0.4), - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - ), - ), - child: Padding( - padding: const EdgeInsets.all(1.0), - child: Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: colorTheme.backgroundBase, - ), - ), - ), - ), - SizedBox( - height: 75, - width: 75, - child: TweenAnimationBuilder( - tween: Tween( - begin: isTimerRunning ? 0 : 1, - end: isTimerRunning - ? _getFractionOfTimeElapsed() - : 1, - ), - duration: const Duration(seconds: 1), - builder: (context, value, _) => - CircularProgressIndicator( - backgroundColor: colorTheme.fillFaintPressed, - value: value, - color: colorTheme.primary400, - strokeWidth: 1.5, - ), - ), - ), - Icon( - Icons.lock, - size: 30, - color: colorTheme.textBase, - ), - ], - ), - ), - const Spacer(), - isTimerRunning - ? Stack( - alignment: Alignment.center, - children: [ - Text( - context.l10n.tooManyIncorrectAttempts, - style: textTheme.small, - ) - .animate( - delay: const Duration(milliseconds: 2000), - ) - .fadeOut( - duration: 400.ms, - curve: Curves.easeInOutCirc, - ), - Text( - _formatTime(remainingTimeInSeconds), - style: textTheme.small, - ) - .animate( - delay: const Duration(milliseconds: 2250), - ) - .fadeIn( - duration: 400.ms, - curve: Curves.easeInOutCirc, - ), - ], - ) - : GestureDetector( - onTap: () => _showLockScreen(source: "tap"), - child: Text( - context.l10n.tapToUnlock, - style: textTheme.small, - ), - ), - const Padding( - padding: EdgeInsets.only(bottom: 24), - ), - ], - ), - ), - ), - ), - ); - } - - void _onLogoutTapped(BuildContext context) { - showChoiceActionSheet( - context, - title: context.l10n.areYouSureYouWantToLogout, - firstButtonLabel: context.l10n.yesLogout, - isCritical: true, - firstButtonOnTap: () async { - await UserService.instance.logout(context); - // To start the app afresh, resetting all state. - Process.killPid(pid, ProcessSignal.sigkill); - }, - ); - } - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - _logger.info(state.toString()); - if (state == AppLifecycleState.resumed && !_isShowingLockScreen) { - // This is triggered either when the lock screen is dismissed or when - // the app is brought to foreground - _hasPlacedAppInBackground = false; - final bool didAuthInLast5Seconds = lastAuthenticatingTime != null && - DateTime.now().millisecondsSinceEpoch - lastAuthenticatingTime! < - 5000; - if (!_hasAuthenticationFailed && !didAuthInLast5Seconds) { - // Show the lock screen again only if the app is resuming from the - // background, and not when the lock screen was explicitly dismissed - if (_lockscreenSetting.getlastInvalidAttemptTime() > - DateTime.now().millisecondsSinceEpoch && - !_isShowingLockScreen) { - final int time = (_lockscreenSetting.getlastInvalidAttemptTime() - - DateTime.now().millisecondsSinceEpoch) ~/ - 1000; - Future.delayed(Duration.zero, () { - startLockTimer(time); - _showLockScreen(source: "lifeCycle"); - }); - } - } else { - _hasAuthenticationFailed = false; // Reset failure state - } - } else if (state == AppLifecycleState.paused || - state == AppLifecycleState.inactive) { - // This is triggered either when the lock screen pops up or when - // the app is pushed to background - if (!_isShowingLockScreen) { - _hasPlacedAppInBackground = true; - _hasAuthenticationFailed = false; // reset failure state - } - } - } - - @override - void dispose() { - _logger.info('disposing'); - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } - - Future startLockTimer(int timeInSeconds) async { - if (isTimerRunning) { - return; - } - - setState(() { - isTimerRunning = true; - remainingTimeInSeconds = timeInSeconds; - }); - - while (remainingTimeInSeconds > 0) { - await Future.delayed(const Duration(seconds: 1)); - setState(() { - remainingTimeInSeconds--; - }); - } - - setState(() { - isTimerRunning = false; - }); - } - - double _getFractionOfTimeElapsed() { - final int totalLockedTime = - lockedTimeInSeconds = pow(2, invalidAttemptCount - 5).toInt() * 30; - if (remainingTimeInSeconds == 0) return 1; - - return 1 - remainingTimeInSeconds / totalLockedTime; - } - - String _formatTime(int seconds) { - final int hours = seconds ~/ 3600; - final int minutes = (seconds % 3600) ~/ 60; - final int remainingSeconds = seconds % 60; - - if (hours > 0) { - return "${hours}h ${minutes}m"; - } else if (minutes > 0) { - return "${minutes}m ${remainingSeconds}s"; - } else { - return "${remainingSeconds}s"; - } - } - - Future _autoLogoutOnMaxInvalidAttempts() async { - _logger.info("Auto logout on max invalid attempts"); - Navigator.of(context, rootNavigator: true).pop('dialog'); - Navigator.of(context).popUntil((route) => route.isFirst); - final dialog = createProgressDialog(context, "Logging out ..."); - await dialog.show(); - await Configuration.instance.logout(); - await dialog.hide(); - } - - Future _showLockScreen({String source = ''}) async { - final int currentTimestamp = DateTime.now().millisecondsSinceEpoch; - _logger.info("Showing lock screen $source $currentTimestamp"); - try { - if (currentTimestamp < _lockscreenSetting.getlastInvalidAttemptTime() && - !_isShowingLockScreen) { - final int remainingTime = - (_lockscreenSetting.getlastInvalidAttemptTime() - - currentTimestamp) ~/ - 1000; - - await startLockTimer(remainingTime); - } - _isShowingLockScreen = true; - final result = isTimerRunning - ? false - : await requestAuthentication( - context, - context.l10n.authToViewSecrets, - isOpeningApp: true, - ); - _logger.finest("LockScreen Result $result $currentTimestamp"); - _isShowingLockScreen = false; - if (result) { - lastAuthenticatingTime = DateTime.now().millisecondsSinceEpoch; - AppLock.of(context)?.didUnlock(); - await _lockscreenSetting.setInvalidAttemptCount(0); - setState(() { - lockedTimeInSeconds = 15; - isTimerRunning = false; - }); - } else { - if (!_hasPlacedAppInBackground) { - // Treat this as a failure only if user did not explicitly - // put the app in background - if (_lockscreenSetting.getInvalidAttemptCount() > 4 && - invalidAttemptCount != - _lockscreenSetting.getInvalidAttemptCount()) { - invalidAttemptCount = _lockscreenSetting.getInvalidAttemptCount(); - - if (invalidAttemptCount > 9) { - await _autoLogoutOnMaxInvalidAttempts(); - return; - } - - lockedTimeInSeconds = pow(2, invalidAttemptCount - 5).toInt() * 30; - await _lockscreenSetting.setLastInvalidAttemptTime( - DateTime.now().millisecondsSinceEpoch + - lockedTimeInSeconds * 1000, - ); - await startLockTimer(lockedTimeInSeconds); - } - _hasAuthenticationFailed = true; - _logger.info("Authentication failed"); - } - } - } catch (e, s) { - _isShowingLockScreen = false; - _logger.severe(e, s); - } - } -} diff --git a/mobile/apps/auth/lib/utils/auth_util.dart b/mobile/apps/auth/lib/utils/auth_util.dart deleted file mode 100644 index 6c797a4328..0000000000 --- a/mobile/apps/auth/lib/utils/auth_util.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:io'; - -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/local_authentication_service.dart'; -import 'package:ente_auth/utils/lock_screen_settings.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_local_authentication/flutter_local_authentication.dart'; -import 'package:local_auth/local_auth.dart'; -import 'package:local_auth_android/local_auth_android.dart'; -import 'package:local_auth_darwin/types/auth_messages_ios.dart'; -import 'package:logging/logging.dart'; - -Future requestAuthentication( - BuildContext context, - String reason, { - bool isOpeningApp = false, - bool isAuthenticatingForInAppChange = false, -}) async { - Logger("AuthUtil").info("Requesting authentication"); - - final String? savedPin = await LockScreenSettings.instance.getPin(); - final String? savedPassword = await LockScreenSettings.instance.getPassword(); - if (savedPassword != null || savedPin != null) { - return await LocalAuthenticationService.instance - .requestEnteAuthForLockScreen( - context, - savedPin, - savedPassword, - isAuthenticatingOnAppLaunch: isOpeningApp, - isAuthenticatingForInAppChange: isAuthenticatingForInAppChange, - ); - } - if (Platform.isMacOS || Platform.isLinux) { - return await FlutterLocalAuthentication().authenticate(); - } else { - await LocalAuthentication().stopAuthentication(); - final l10n = context.l10n; - return await LocalAuthentication().authenticate( - localizedReason: reason, - authMessages: [ - AndroidAuthMessages( - biometricHint: l10n.androidBiometricHint, - biometricNotRecognized: l10n.androidBiometricNotRecognized, - biometricRequiredTitle: l10n.androidBiometricRequiredTitle, - biometricSuccess: l10n.androidBiometricSuccess, - cancelButton: l10n.androidCancelButton, - deviceCredentialsRequiredTitle: - l10n.androidDeviceCredentialsRequiredTitle, - deviceCredentialsSetupDescription: - l10n.androidDeviceCredentialsSetupDescription, - goToSettingsButton: l10n.goToSettings, - goToSettingsDescription: l10n.androidGoToSettingsDescription, - signInTitle: l10n.androidSignInTitle, - ), - IOSAuthMessages( - localizedFallbackTitle: l10n.enterPassword, - goToSettingsButton: l10n.goToSettings, - goToSettingsDescription: l10n.goToSettings, - lockOut: l10n.iOSLockOut, - // cancelButton default value is "Ok" - cancelButton: l10n.iOSOkButton, - ), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/utils/lock_screen_settings.dart b/mobile/apps/auth/lib/utils/lock_screen_settings.dart deleted file mode 100644 index 4f2f81b11d..0000000000 --- a/mobile/apps/auth/lib/utils/lock_screen_settings.dart +++ /dev/null @@ -1,253 +0,0 @@ -import "dart:convert"; -import "dart:io"; -import "dart:typed_data"; - -import "package:ente_auth/core/configuration.dart"; -import "package:ente_auth/utils/platform_util.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import "package:ente_events/event_bus.dart"; -import "package:ente_events/models/signed_out_event.dart"; -import "package:flutter/material.dart"; -import "package:flutter_secure_storage/flutter_secure_storage.dart"; -import "package:privacy_screen/privacy_screen.dart"; -import "package:shared_preferences/shared_preferences.dart"; - -class LockScreenSettings { - LockScreenSettings._privateConstructor(); - - static final LockScreenSettings instance = - LockScreenSettings._privateConstructor(); - static const password = "ls_password"; - static const pin = "ls_pin"; - static const saltKey = "ls_salt"; - static const keyInvalidAttempts = "ls_invalid_attempts"; - static const lastInvalidAttemptTime = "ls_last_invalid_attempt_time"; - static const autoLockTime = "ls_auto_lock_time"; - static const keyHideAppContent = "ls_hide_app_content"; - static const keyAppLockSet = "ls_is_app_lock_set"; - static const keyHasMigratedLockScreenChanges = - "ls_has_migrated_lock_screen_changes"; - static const keyShowOfflineModeWarning = "ls_show_offline_mode_warning"; - static const keyShouldShowLockScreen = "should_show_lock_screen"; - static const String kIsLightMode = "is_light_mode"; - - final List autoLockDurations = const [ - Duration(milliseconds: 650), - Duration(seconds: 5), - Duration(seconds: 15), - Duration(minutes: 1), - Duration(minutes: 5), - Duration(minutes: 30), - ]; - - late SharedPreferences _preferences; - late FlutterSecureStorage _secureStorage; - - Future init() async { - _secureStorage = const FlutterSecureStorage(); - _preferences = await SharedPreferences.getInstance(); - - ///Workaround for privacyScreen not working when app is killed and opened. - await setHideAppContent(getShouldHideAppContent()); - - /// Function to Check if the migration for lock screen changes has - /// already been done by checking a stored boolean value. - await runLockScreenChangesMigration(); - - await _clearLsDataInKeychainIfFreshInstall(); - - Bus.instance.on().listen((event) { - removePinAndPassword(); - }); - } - - Future setOfflineModeWarningStatus(bool value) async { - await _preferences.setBool(keyShowOfflineModeWarning, value); - } - - bool getOfflineModeWarningStatus() { - return _preferences.getBool(keyShowOfflineModeWarning) ?? true; - } - - Future runLockScreenChangesMigration() async { - if (_preferences.getBool(keyHasMigratedLockScreenChanges) != null) { - return; - } - - final bool passwordEnabled = await isPasswordSet(); - final bool pinEnabled = await isPinSet(); - final bool systemLockEnabled = shouldShowSystemLockScreen(); - - if (passwordEnabled || pinEnabled || systemLockEnabled) { - await setAppLockEnabled(true); - } - - await _preferences.setBool(keyHasMigratedLockScreenChanges, true); - } - - Future setLightMode(bool isLightMode) async { - if (isLightMode != (_preferences.getBool(kIsLightMode) ?? true)) { - await _preferences.setBool(kIsLightMode, isLightMode); - } - } - - Future setHideAppContent(bool hideContent) async { - if (PlatformUtil.isDesktop()) return; - final bool isLightMode = _preferences.getBool(kIsLightMode) ?? true; - !hideContent - ? PrivacyScreen.instance.disable() - : await PrivacyScreen.instance.enable( - iosOptions: const PrivacyIosOptions( - enablePrivacy: true, - ), - androidOptions: const PrivacyAndroidOptions( - enableSecure: true, - ), - backgroundColor: - isLightMode ? const Color(0xffffffff) : const Color(0xff000000), - blurEffect: isLightMode - ? PrivacyBlurEffect.extraLight - : PrivacyBlurEffect.extraLight, - ); - await _preferences.setBool(keyHideAppContent, hideContent); - } - - bool getShouldHideAppContent() { - return _preferences.getBool(keyHideAppContent) ?? true; - } - - Future setAutoLockTime(Duration duration) async { - await _preferences.setInt(autoLockTime, duration.inMilliseconds); - } - - int getAutoLockTime() { - return _preferences.getInt(autoLockTime) ?? 5000; - } - - Future setLastInvalidAttemptTime(int time) async { - await _preferences.setInt(lastInvalidAttemptTime, time); - } - - int getlastInvalidAttemptTime() { - return _preferences.getInt(lastInvalidAttemptTime) ?? 0; - } - - int getInvalidAttemptCount() { - return _preferences.getInt(keyInvalidAttempts) ?? 0; - } - - Future setInvalidAttemptCount(int count) async { - await _preferences.setInt(keyInvalidAttempts, count); - } - - Future setAppLockEnabled(bool value) async { - await _preferences.setBool(keyAppLockSet, value); - } - - bool getIsAppLockSet() { - return _preferences.getBool(keyAppLockSet) ?? false; - } - - static Uint8List _generateSalt() { - return sodium.randombytes.buf(sodium.crypto.pwhash.saltBytes); - } - - Future setPin(String userPin) async { - await _secureStorage.delete(key: saltKey); - final salt = _generateSalt(); - - final hash = cryptoPwHash( - utf8.encode(userPin), - salt, - sodium.crypto.pwhash.memLimitInteractive, - sodium.crypto.pwhash.opsLimitSensitive, - sodium, - ); - final String saltPin = base64Encode(salt); - final String hashedPin = base64Encode(hash); - - await _secureStorage.write(key: saltKey, value: saltPin); - await _secureStorage.write(key: pin, value: hashedPin); - await _secureStorage.delete(key: password); - - return; - } - - Future getSalt() async { - final String? salt = await _secureStorage.read(key: saltKey); - if (salt == null) return null; - return base64Decode(salt); - } - - Future getPin() async { - return _secureStorage.read(key: pin); - } - - Future setPassword(String pass) async { - await _secureStorage.delete(key: saltKey); - final salt = _generateSalt(); - - final hash = cryptoPwHash( - utf8.encode(pass), - salt, - sodium.crypto.pwhash.memLimitInteractive, - sodium.crypto.pwhash.opsLimitSensitive, - sodium, - ); - - await _secureStorage.write(key: saltKey, value: base64Encode(salt)); - await _secureStorage.write(key: password, value: base64Encode(hash)); - await _secureStorage.delete(key: pin); - - return; - } - - Future getPassword() async { - return _secureStorage.read(key: password); - } - - Future removePinAndPassword() async { - await _secureStorage.delete(key: saltKey); - await _secureStorage.delete(key: pin); - await _secureStorage.delete(key: password); - } - - Future isPinSet() async { - return await _secureStorage.containsKey(key: pin); - } - - Future isPasswordSet() async { - return await _secureStorage.containsKey(key: password); - } - - Future shouldShowLockScreen() async { - final bool isPin = await isPinSet(); - final bool isPass = await isPasswordSet(); - return isPin || isPass || shouldShowSystemLockScreen(); - } - - bool shouldShowSystemLockScreen() { - if (_preferences.containsKey(keyShouldShowLockScreen)) { - return _preferences.getBool(keyShouldShowLockScreen)!; - } else { - return false; - } - } - - Future setSystemLockScreen(bool value) { - return _preferences.setBool(keyShouldShowLockScreen, value); - } - - // If the app was uninstalled (without logging out if it was used with - // backups), keychain items of the app persist in the keychain. To avoid using - // old keychain items, we delete them on reinstall. - Future _clearLsDataInKeychainIfFreshInstall() async { - if ((Platform.isIOS || Platform.isMacOS) && - !Configuration.instance.isLoggedIn() && - !Configuration.instance.hasOptedForOfflineMode()) { - await _secureStorage.delete(key: password); - await _secureStorage.delete(key: pin); - await _secureStorage.delete(key: saltKey); - } - } -} diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index b670dcb123..676273f726 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -398,6 +398,13 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + ente_accounts: + dependency: transitive + description: + path: "../../packages/accounts" + relative: true + source: path + version: "1.0.0" ente_base: dependency: "direct main" description: @@ -428,6 +435,13 @@ packages: relative: true source: path version: "1.0.0" + ente_lock_screen: + dependency: "direct main" + description: + path: "../../packages/lock_screen" + relative: true + source: path + version: "1.0.0" ente_logging: dependency: "direct main" description: @@ -442,6 +456,27 @@ packages: relative: true source: path version: "1.0.0" + ente_strings: + dependency: transitive + description: + path: "../../packages/strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: transitive + description: + path: "../../packages/ui" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: transitive + description: + path: "../../packages/utils" + relative: true + source: path + version: "1.0.0" event_bus: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 6cabe14406..c89781af75 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -34,6 +34,8 @@ dependencies: url: https://github.com/ente-io/ente_crypto_dart.git ente_events: path: ../../packages/events + ente_lock_screen: + path: ../../packages/lock_screen ente_logging: path: ../../packages/logging ente_network: diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index 56bcf90e1f..a07b26b3e9 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -252,19 +252,21 @@ packages: flutter_inappwebview: dependency: "direct main" description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" + path: flutter_inappwebview + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "6.2.0-beta.3" flutter_inappwebview_android: dependency: transitive description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" + path: flutter_inappwebview_android + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -276,43 +278,48 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_ios + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_macos: dependency: transitive description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_macos + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" + path: flutter_inappwebview_platform_interface + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.4.0-beta.3" flutter_inappwebview_web: dependency: transitive description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_web + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_windows: dependency: transitive description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" + path: flutter_inappwebview_windows + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "0.7.0-beta.3" flutter_lints: dependency: "direct dev" description: diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml index 15be90cfc2..8cc504a4b0 100644 --- a/mobile/packages/ui/pubspec.yaml +++ b/mobile/packages/ui/pubspec.yaml @@ -21,7 +21,11 @@ dependencies: path: ../../packages/utils flutter: sdk: flutter - flutter_inappwebview: ^6.1.5 + flutter_inappwebview: + git: + url: https://github.com/pichillilorenzo/flutter_inappwebview.git + path: flutter_inappwebview + ref: 3e6c4c4a25340cd363af9d38891d88498b90be26 fluttertoast: ^8.1.1 modal_bottom_sheet: ^3.0.0 shared_preferences: ^2.5.3 From 399148aa5990eae8b2da5be49064ac4947e5720c Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 31 Jul 2025 19:44:40 +0530 Subject: [PATCH 104/164] Introduced lock screen package from packages/lockscreen From e87be4b9af4a6c602e271e92dfc0d063fbd00b00 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 31 Jul 2025 19:47:33 +0530 Subject: [PATCH 105/164] Update flutter_inappwebview dependencies --- mobile/packages/lock_screen/pubspec.lock | 77 +++++++++++++----------- mobile/packages/utils/pubspec.lock | 77 +++++++++++++----------- 2 files changed, 84 insertions(+), 70 deletions(-) diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index adae66b006..40cf271ac0 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -337,19 +337,21 @@ packages: flutter_inappwebview: dependency: transitive description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" + path: flutter_inappwebview + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "6.2.0-beta.3" flutter_inappwebview_android: dependency: transitive description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" + path: flutter_inappwebview_android + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -361,43 +363,48 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_ios + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_macos: dependency: transitive description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_macos + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" + path: flutter_inappwebview_platform_interface + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.4.0-beta.3" flutter_inappwebview_web: dependency: transitive description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_web + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_windows: dependency: transitive description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" + path: flutter_inappwebview_windows + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "0.7.0-beta.3" flutter_lints: dependency: "direct dev" description: diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index 982e79b216..f846d030da 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -252,19 +252,21 @@ packages: flutter_inappwebview: dependency: transitive description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" + path: flutter_inappwebview + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "6.2.0-beta.3" flutter_inappwebview_android: dependency: transitive description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" + path: flutter_inappwebview_android + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -276,43 +278,48 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_ios + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_macos: dependency: transitive description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_macos + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" + path: flutter_inappwebview_platform_interface + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.4.0-beta.3" flutter_inappwebview_web: dependency: transitive description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_web + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_windows: dependency: transitive description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" + path: flutter_inappwebview_windows + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "0.7.0-beta.3" flutter_lints: dependency: "direct dev" description: From b02acc579fd3786a344bac4c1ceff49f98ae852d Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:25:24 +0530 Subject: [PATCH 106/164] Update ente_accounts dependency --- mobile/apps/auth/pubspec.lock | 2 +- mobile/apps/auth/pubspec.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 676273f726..3954c018fe 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -399,7 +399,7 @@ packages: source: hosted version: "3.0.0" ente_accounts: - dependency: transitive + dependency: "direct main" description: path: "../../packages/accounts" relative: true diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index c89781af75..785ab8017b 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -25,6 +25,8 @@ dependencies: dotted_border: ^2.0.0+2 dropdown_button2: ^2.3.9 email_validator: ^3.0.0 + ente_accounts: + path: ../../packages/accounts ente_base: path: ../../packages/base ente_configuration: From d430936ae8cb22119367a5414d36641d7c3ca01c Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:26:20 +0530 Subject: [PATCH 107/164] Refractor accounts section to use common code from `packages/accounts` --- .../auth/lib/models/account/two_factor.dart | 11 - .../apps/auth/lib/models/delete_account.dart | 9 - mobile/apps/auth/lib/models/sessions.dart | 45 - .../auth/lib/models/set_keys_request.dart | 25 - .../lib/models/set_recovery_key_request.dart | 22 - mobile/apps/auth/lib/models/subscription.dart | 65 -- mobile/apps/auth/lib/models/typedefs.dart | 7 - mobile/apps/auth/lib/models/user_details.dart | 146 --- .../lib/onboarding/view/onboarding_page.dart | 4 +- .../auth/lib/services/billing_service.dart | 4 +- .../apps/auth/lib/services/user_service.dart | 18 +- .../lib/ui/account/change_email_dialog.dart | 82 -- .../lib/ui/account/delete_account_page.dart | 251 ----- .../auth/lib/ui/account/email_entry_page.dart | 1002 ++++++++--------- .../auth/lib/ui/account/sessions_page.dart | 228 ---- mobile/apps/auth/lib/ui/common/dialogs.dart | 4 +- .../ui/components/buttons/button_widget.dart | 4 +- .../auth/lib/ui/components/dialog_widget.dart | 4 +- .../lib/ui/components/menu_item_widget.dart | 4 +- .../lib/ui/components/text_input_widget.dart | 4 +- .../ui/components/toggle_switch_widget.dart | 4 +- mobile/apps/auth/lib/ui/passkey_page.dart | 4 +- .../ui/settings/account_section_widget.dart | 9 +- .../settings/notification_banner_widget.dart | 4 +- .../ui/settings/security_section_widget.dart | 10 +- .../ui/two_factor_authentication_page.dart | 161 --- .../auth/lib/ui/two_factor_recovery_page.dart | 112 -- mobile/apps/auth/lib/utils/dialog_util.dart | 4 +- mobile/packages/accounts/pubspec.lock | 77 +- 29 files changed, 584 insertions(+), 1740 deletions(-) delete mode 100644 mobile/apps/auth/lib/models/account/two_factor.dart delete mode 100644 mobile/apps/auth/lib/models/delete_account.dart delete mode 100644 mobile/apps/auth/lib/models/sessions.dart delete mode 100644 mobile/apps/auth/lib/models/set_keys_request.dart delete mode 100644 mobile/apps/auth/lib/models/set_recovery_key_request.dart delete mode 100644 mobile/apps/auth/lib/models/subscription.dart delete mode 100644 mobile/apps/auth/lib/models/typedefs.dart delete mode 100644 mobile/apps/auth/lib/models/user_details.dart delete mode 100644 mobile/apps/auth/lib/ui/account/change_email_dialog.dart delete mode 100644 mobile/apps/auth/lib/ui/account/delete_account_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/sessions_page.dart delete mode 100644 mobile/apps/auth/lib/ui/two_factor_authentication_page.dart delete mode 100644 mobile/apps/auth/lib/ui/two_factor_recovery_page.dart diff --git a/mobile/apps/auth/lib/models/account/two_factor.dart b/mobile/apps/auth/lib/models/account/two_factor.dart deleted file mode 100644 index 45f7c8fde8..0000000000 --- a/mobile/apps/auth/lib/models/account/two_factor.dart +++ /dev/null @@ -1,11 +0,0 @@ -enum TwoFactorType { totp, passkey } - -// ToString for TwoFactorType -String twoFactorTypeToString(TwoFactorType type) { - switch (type) { - case TwoFactorType.totp: - return "totp"; - case TwoFactorType.passkey: - return "passkey"; - } -} diff --git a/mobile/apps/auth/lib/models/delete_account.dart b/mobile/apps/auth/lib/models/delete_account.dart deleted file mode 100644 index 71ca04e9c3..0000000000 --- a/mobile/apps/auth/lib/models/delete_account.dart +++ /dev/null @@ -1,9 +0,0 @@ -class DeleteChallengeResponse { - final bool allowDelete; - final String encryptedChallenge; - - DeleteChallengeResponse({ - required this.allowDelete, - required this.encryptedChallenge, - }); -} diff --git a/mobile/apps/auth/lib/models/sessions.dart b/mobile/apps/auth/lib/models/sessions.dart deleted file mode 100644 index 56ba8a31a1..0000000000 --- a/mobile/apps/auth/lib/models/sessions.dart +++ /dev/null @@ -1,45 +0,0 @@ -class Sessions { - final List sessions; - - Sessions( - this.sessions, - ); - - factory Sessions.fromMap(Map map) { - if (map["sessions"] == null) { - throw Exception('\'map["sessions"]\' must not be null'); - } - return Sessions( - List.from(map['sessions']?.map((x) => Session.fromMap(x))), - ); - } -} - -class Session { - final String token; - final int creationTime; - final String ip; - final String ua; - final String prettyUA; - final int lastUsedTime; - - Session( - this.token, - this.creationTime, - this.ip, - this.ua, - this.prettyUA, - this.lastUsedTime, - ); - - factory Session.fromMap(Map map) { - return Session( - map['token'], - map['creationTime'], - map['ip'], - map['ua'], - map['prettyUA'], - map['lastUsedTime'], - ); - } -} diff --git a/mobile/apps/auth/lib/models/set_keys_request.dart b/mobile/apps/auth/lib/models/set_keys_request.dart deleted file mode 100644 index e782319f50..0000000000 --- a/mobile/apps/auth/lib/models/set_keys_request.dart +++ /dev/null @@ -1,25 +0,0 @@ -class SetKeysRequest { - final String kekSalt; - final String encryptedKey; - final String keyDecryptionNonce; - final int memLimit; - final int opsLimit; - - SetKeysRequest({ - required this.kekSalt, - required this.encryptedKey, - required this.keyDecryptionNonce, - required this.memLimit, - required this.opsLimit, - }); - - Map toMap() { - return { - 'kekSalt': kekSalt, - 'encryptedKey': encryptedKey, - 'keyDecryptionNonce': keyDecryptionNonce, - 'memLimit': memLimit, - 'opsLimit': opsLimit, - }; - } -} diff --git a/mobile/apps/auth/lib/models/set_recovery_key_request.dart b/mobile/apps/auth/lib/models/set_recovery_key_request.dart deleted file mode 100644 index 1a03e4e36c..0000000000 --- a/mobile/apps/auth/lib/models/set_recovery_key_request.dart +++ /dev/null @@ -1,22 +0,0 @@ -class SetRecoveryKeyRequest { - final String masterKeyEncryptedWithRecoveryKey; - final String masterKeyDecryptionNonce; - final String recoveryKeyEncryptedWithMasterKey; - final String recoveryKeyDecryptionNonce; - - SetRecoveryKeyRequest( - this.masterKeyEncryptedWithRecoveryKey, - this.masterKeyDecryptionNonce, - this.recoveryKeyEncryptedWithMasterKey, - this.recoveryKeyDecryptionNonce, - ); - - Map toMap() { - return { - 'masterKeyEncryptedWithRecoveryKey': masterKeyEncryptedWithRecoveryKey, - 'masterKeyDecryptionNonce': masterKeyDecryptionNonce, - 'recoveryKeyEncryptedWithMasterKey': recoveryKeyEncryptedWithMasterKey, - 'recoveryKeyDecryptionNonce': recoveryKeyDecryptionNonce, - }; - } -} diff --git a/mobile/apps/auth/lib/models/subscription.dart b/mobile/apps/auth/lib/models/subscription.dart deleted file mode 100644 index 3804f9a2e8..0000000000 --- a/mobile/apps/auth/lib/models/subscription.dart +++ /dev/null @@ -1,65 +0,0 @@ -const freeProductID = "free"; -const stripe = "stripe"; -const appStore = "appstore"; -const playStore = "playstore"; - -class Subscription { - final String productID; - final int storage; - final String originalTransactionID; - final String paymentProvider; - final int expiryTime; - final String price; - final String period; - final Attributes? attributes; - - Subscription({ - required this.productID, - required this.storage, - required this.originalTransactionID, - required this.paymentProvider, - required this.expiryTime, - required this.price, - required this.period, - this.attributes, - }); - - bool isValid() { - return expiryTime > DateTime.now().microsecondsSinceEpoch; - } - - bool isYearlyPlan() { - return 'year' == period; - } - - static fromMap(Map? map) { - if (map == null) return null; - return Subscription( - productID: map['productID'], - storage: map['storage'], - originalTransactionID: map['originalTransactionID'], - paymentProvider: map['paymentProvider'], - expiryTime: map['expiryTime'], - price: map['price'], - period: map['period'], - attributes: map["attributes"] != null - ? Attributes.fromJson(map["attributes"]) - : null, - ); - } -} - -class Attributes { - bool? isCancelled; - String? customerID; - - Attributes({ - this.isCancelled, - this.customerID, - }); - - Attributes.fromJson(dynamic json) { - isCancelled = json["isCancelled"]; - customerID = json["customerID"]; - } -} diff --git a/mobile/apps/auth/lib/models/typedefs.dart b/mobile/apps/auth/lib/models/typedefs.dart deleted file mode 100644 index 7a42a1fe5e..0000000000 --- a/mobile/apps/auth/lib/models/typedefs.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'dart:async'; - -typedef FutureVoidCallback = Future Function(); -typedef BoolCallBack = bool Function(); -typedef FutureVoidCallbackParamStr = Future Function(String); -typedef VoidCallbackParamStr = void Function(String); -typedef FutureOrVoidCallback = FutureOr Function(); diff --git a/mobile/apps/auth/lib/models/user_details.dart b/mobile/apps/auth/lib/models/user_details.dart deleted file mode 100644 index e50d3ee295..0000000000 --- a/mobile/apps/auth/lib/models/user_details.dart +++ /dev/null @@ -1,146 +0,0 @@ -import 'dart:convert'; -import 'dart:math'; - -import 'package:collection/collection.dart'; -import 'package:ente_auth/models/subscription.dart'; - -class UserDetails { - final String email; - final int usage; - final int fileCount; - final int sharedCollectionsCount; - final Subscription subscription; - final FamilyData? familyData; - final ProfileData? profileData; - - UserDetails( - this.email, - this.usage, - this.fileCount, - this.sharedCollectionsCount, - this.subscription, - this.familyData, - this.profileData, - ); - - bool isPartOfFamily() { - return familyData?.members?.isNotEmpty ?? false; - } - - bool isFamilyAdmin() { - assert(isPartOfFamily(), "verify user is part of family before calling"); - final FamilyMember currentUserMember = familyData!.members! - .firstWhere((element) => element.email.trim() == email.trim()); - return currentUserMember.isAdmin; - } - - // getFamilyOrPersonalUsage will return total usage for family if user - // belong to family group. Otherwise, it will return storage consumed by - // current user - int getFamilyOrPersonalUsage() { - return isPartOfFamily() ? familyData!.getTotalUsage() : usage; - } - - int getFreeStorage() { - return max( - isPartOfFamily() - ? (familyData!.storage - familyData!.getTotalUsage()) - : (subscription.storage - (usage)), - 0, - ); - } - - int getTotalStorage() { - return isPartOfFamily() ? familyData!.storage : subscription.storage; - } - - factory UserDetails.fromMap(Map map) { - return UserDetails( - map['email'] as String, - map['usage'] as int, - (map['fileCount'] ?? 0) as int, - (map['sharedCollectionsCount'] ?? 0) as int, - Subscription.fromMap(map['subscription']), - FamilyData.fromMap(map['familyData']), - ProfileData.fromJson(map['profileData']), - ); - } - -} - -class FamilyMember { - final String email; - final int usage; - final String id; - final bool isAdmin; - - FamilyMember(this.email, this.usage, this.id, this.isAdmin); - - factory FamilyMember.fromMap(Map map) { - return FamilyMember( - (map['email'] ?? '') as String, - map['usage'] as int, - map['id'] as String, - map['isAdmin'] as bool, - ); - } -} -class ProfileData { - bool canDisableEmailMFA; - bool isEmailMFAEnabled; - bool isTwoFactorEnabled; - - // Constructor with default values - ProfileData({ - this.canDisableEmailMFA = false, - this.isEmailMFAEnabled = false, - this.isTwoFactorEnabled = false, - }); - - // Factory method to create ProfileData instance from JSON - factory ProfileData.fromJson(Map? json) { - if (json == null) null; - - return ProfileData( - canDisableEmailMFA: json!['canDisableEmailMFA'] ?? false, - isEmailMFAEnabled: json['isEmailMFAEnabled'] ?? false, - isTwoFactorEnabled: json['isTwoFactorEnabled'] ?? false, - ); - } - - // Method to convert ProfileData instance to JSON - Map toJson() { - return { - 'canDisableEmailMFA': canDisableEmailMFA, - 'isEmailMFAEnabled': isEmailMFAEnabled, - 'isTwoFactorEnabled': isTwoFactorEnabled, - }; - } - String toJsonString() => json.encode(toJson()); -} -class FamilyData { - final List? members; - - // Storage available based on the family plan - final int storage; - final int expiryTime; - - FamilyData(this.members, this.storage, this.expiryTime); - - int getTotalUsage() { - return members!.map((e) => e.usage).toList().sum; - } - - static fromMap(Map? map) { - if (map == null) return null; - assert(map['members'] != null && map['members'].length >= 0); - final members = List.from( - map['members'].map((x) => FamilyMember.fromMap(x)), - ); - return FamilyData( - members, - map['storage'] as int, - map['expiryTime'] as int, - ); - } -} diff --git a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart index 0ce09dd45c..9037cf2652 100644 --- a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart +++ b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; +import 'package:ente_accounts/pages/email_entry_page.dart'; import 'package:ente_auth/app/view/app.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; @@ -8,7 +9,6 @@ import 'package:ente_auth/events/trigger_logout_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/theme/text_style.dart'; -import 'package:ente_auth/ui/account/email_entry_page.dart'; import 'package:ente_auth/ui/account/login_page.dart'; import 'package:ente_auth/ui/account/logout_dialog.dart'; import 'package:ente_auth/ui/account/password_entry_page.dart'; @@ -260,7 +260,7 @@ class _OnboardingPageState extends State { void _navigateToSignUpPage() { Widget page; if (Configuration.instance.getEncryptedToken() == null) { - page = const EmailEntryPage(); + page = EmailEntryPage(Configuration.instance); } else { // No key if (Configuration.instance.getKeyAttributes() == null) { diff --git a/mobile/apps/auth/lib/services/billing_service.dart b/mobile/apps/auth/lib/services/billing_service.dart index 39929dc5aa..8aad05e5c7 100644 --- a/mobile/apps/auth/lib/services/billing_service.dart +++ b/mobile/apps/auth/lib/services/billing_service.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:dio/dio.dart'; +import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/models/billing_plan.dart'; -import 'package:ente_auth/models/subscription.dart'; +import 'package:ente_auth/models/billing_plan.dart'; import 'package:ente_network/network.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/services/user_service.dart b/mobile/apps/auth/lib/services/user_service.dart index 2becb8e590..c79016b3d4 100644 --- a/mobile/apps/auth/lib/services/user_service.dart +++ b/mobile/apps/auth/lib/services/user_service.dart @@ -4,17 +4,19 @@ import "dart:math"; import 'package:bip39/bip39.dart' as bip39; import 'package:dio/dio.dart'; +import 'package:ente_accounts/models/delete_account.dart'; +import 'package:ente_accounts/models/sessions.dart'; +import 'package:ente_accounts/models/set_keys_request.dart'; +import 'package:ente_accounts/models/set_recovery_key_request.dart'; +import 'package:ente_accounts/models/two_factor.dart'; +import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; +import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/errors.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/account/two_factor.dart'; -import 'package:ente_auth/models/api/user/srp.dart'; -import 'package:ente_auth/models/delete_account.dart'; -import 'package:ente_auth/models/sessions.dart'; -import 'package:ente_auth/models/set_keys_request.dart'; -import 'package:ente_auth/models/set_recovery_key_request.dart'; -import 'package:ente_auth/models/user_details.dart'; +import 'package:ente_auth/models/api/user/srp.dart'; import 'package:ente_auth/ui/account/login_page.dart'; import 'package:ente_auth/ui/account/ott_verification_page.dart'; import 'package:ente_auth/ui/account/password_entry_page.dart'; @@ -23,8 +25,6 @@ import 'package:ente_auth/ui/account/recovery_page.dart'; import 'package:ente_auth/ui/common/progress_dialog.dart'; import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/ui/passkey_page.dart'; -import 'package:ente_auth/ui/two_factor_authentication_page.dart'; -import 'package:ente_auth/ui/two_factor_recovery_page.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_base/models/key_attributes.dart'; diff --git a/mobile/apps/auth/lib/ui/account/change_email_dialog.dart b/mobile/apps/auth/lib/ui/account/change_email_dialog.dart deleted file mode 100644 index 5277e0fcb6..0000000000 --- a/mobile/apps/auth/lib/ui/account/change_email_dialog.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/email_util.dart'; -import 'package:flutter/material.dart'; - -class ChangeEmailDialog extends StatefulWidget { - const ChangeEmailDialog({super.key}); - - @override - State createState() => _ChangeEmailDialogState(); -} - -class _ChangeEmailDialogState extends State { - String _email = ""; - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return AlertDialog( - title: Text(l10n.enterNewEmailHint), - content: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextFormField( - decoration: InputDecoration( - hintText: l10n.email, - hintStyle: const TextStyle( - color: Colors.white30, - ), - contentPadding: const EdgeInsets.all(12), - ), - onChanged: (value) { - setState(() { - _email = value; - }); - }, - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: _email, - autofocus: true, - ), - ], - ), - ), - actions: [ - TextButton( - child: Text( - l10n.cancel, - style: const TextStyle( - color: Colors.redAccent, - ), - ), - onPressed: () { - Navigator.pop(context); - }, - ), - TextButton( - child: Text( - l10n.verify, - style: const TextStyle( - color: Colors.purple, - ), - ), - onPressed: () { - if (!isValidEmail(_email)) { - showErrorDialog( - context, - l10n.invalidEmailTitle, - l10n.invalidEmailMessage, - ); - return; - } - UserService.instance.sendOtt(context, _email, isChangeEmail: true); - }, - ), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/delete_account_page.dart b/mobile/apps/auth/lib/ui/account/delete_account_page.dart deleted file mode 100644 index d11b0ccaad..0000000000 --- a/mobile/apps/auth/lib/ui/account/delete_account_page.dart +++ /dev/null @@ -1,251 +0,0 @@ -import 'dart:convert'; - -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/delete_account.dart'; - import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/common/dialogs.dart'; -import 'package:ente_auth/ui/common/gradient_button.dart'; -import 'package:ente_auth/utils/email_util.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_lock_screen/local_authentication_service.dart'; -import 'package:flutter/material.dart'; - -class DeleteAccountPage extends StatelessWidget { - const DeleteAccountPage({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Scaffold( - appBar: AppBar( - elevation: 0, - title: Text(l10n.deleteAccount), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - body: Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24), - child: Center( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Image.asset( - 'assets/broken_heart.png', - width: 200, - ), - const SizedBox( - height: 24, - ), - Center( - child: Text( - l10n.deleteAccountQuery, - style: Theme.of(context).textTheme.titleMedium, - ), - ), - const SizedBox( - height: 12, - ), - RichText( - // textAlign: TextAlign.center, - text: TextSpan( - children: const [ - TextSpan(text: "Please write to us at "), - TextSpan( - text: "feedback@ente.io", - style: TextStyle(color: Color.fromRGBO(29, 185, 84, 1)), - ), - TextSpan( - text: ", maybe there is a way we can help.", - ), - ], - style: Theme.of(context).textTheme.titleMedium, - ), - ), - const SizedBox( - height: 24, - ), - GradientButton( - text: l10n.yesSendFeedbackAction, - iconData: Icons.check, - onTap: () async { - await sendEmail( - context, - to: 'feedback@ente.io', - subject: '[Feedback]', - ); - }, - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 8), - ), - InkWell( - child: SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - style: OutlinedButton.styleFrom( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - side: const BorderSide( - color: Colors.redAccent, - ), - padding: const EdgeInsets.symmetric( - vertical: 18, - horizontal: 10, - ), - backgroundColor: Colors.white, - ), - label: Text( - l10n.noDeleteAccountAction, - style: const TextStyle( - color: Colors.redAccent, // same for both themes - ), - textAlign: TextAlign.center, - ), - onPressed: () async => {await _initiateDelete(context)}, - icon: const Icon( - Icons.no_accounts, - color: Colors.redAccent, - ), - ), - ), - ), - ], - ), - ), - ), - ); - } - - Future _initiateDelete(BuildContext context) async { - final deleteChallengeResponse = - await UserService.instance.getDeleteChallenge(context); - if (deleteChallengeResponse == null) { - return; - } - if (deleteChallengeResponse.allowDelete) { - await _confirmAndDelete(context, deleteChallengeResponse); - } else { - await _requestEmailForDeletion(context); - } - } - - Future _confirmAndDelete( - BuildContext context, - DeleteChallengeResponse response, - ) async { - final l10n = context.l10n; - final hasAuthenticated = - await LocalAuthenticationService.instance.requestLocalAuthentication( - context, - l10n.initiateAccountDeleteTitle, - ); - - await PlatformUtil.refocusWindows(); - - if (hasAuthenticated) { - final choice = await showChoiceDialogOld( - context, - l10n.confirmAccountDeleteTitle, - l10n.confirmAccountDeleteMessage, - firstAction: l10n.cancel, - secondAction: l10n.delete, - firstActionColor: Theme.of(context).colorScheme.onSurface, - secondActionColor: Colors.red, - ); - if (choice != DialogUserChoice.secondChoice) { - return; - } - final decryptChallenge = CryptoUtil.openSealSync( - CryptoUtil.base642bin(response.encryptedChallenge), - CryptoUtil.base642bin( - Configuration.instance.getKeyAttributes()!.publicKey, - ), - Configuration.instance.getSecretKey()!, - ); - final challengeResponseStr = utf8.decode(decryptChallenge); - await UserService.instance.deleteAccount(context, challengeResponseStr); - } - } - - Future _requestEmailForDeletion(BuildContext context) async { - final l10n = context.l10n; - final AlertDialog alert = AlertDialog( - title: Text( - l10n.deleteAccount, - style: const TextStyle( - color: Colors.red, - ), - ), - content: RichText( - text: TextSpan( - children: [ - const TextSpan( - text: "Please send an email to ", - ), - TextSpan( - text: "account-deletion@ente.io", - style: TextStyle( - color: Colors.orange[300], - ), - ), - const TextSpan( - text: - " from your registered email address.\n\nYour request will be processed within 72 hours.", - ), - ], - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - height: 1.5, - fontSize: 16, - ), - ), - ), - actions: [ - TextButton( - child: Text( - l10n.sendEmail, - style: const TextStyle( - color: Colors.red, - ), - ), - onPressed: () async { - Navigator.of(context, rootNavigator: true).pop('dialog'); - await sendEmail( - context, - to: 'account-deletion@ente.io', - subject: '[Delete account]', - ); - }, - ), - TextButton( - child: Text( - l10n.ok, - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - ), - ), - onPressed: () { - Navigator.of(context, rootNavigator: true).pop('dialog'); - }, - ), - ], - ); - // ignore: unawaited_futures - showDialog( - context: context, - builder: (BuildContext context) { - return alert; - }, - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/email_entry_page.dart b/mobile/apps/auth/lib/ui/account/email_entry_page.dart index 2283f4ba09..24cc77cbce 100644 --- a/mobile/apps/auth/lib/ui/account/email_entry_page.dart +++ b/mobile/apps/auth/lib/ui/account/email_entry_page.dart @@ -1,516 +1,516 @@ -import 'package:email_validator/email_validator.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:password_strength/password_strength.dart'; -import 'package:step_progress_indicator/step_progress_indicator.dart'; -import "package:styled_text/styled_text.dart"; +// import 'package:email_validator/email_validator.dart'; +// import 'package:ente_auth/core/configuration.dart'; +// import 'package:ente_auth/ente_theme_data.dart'; +// import 'package:ente_auth/l10n/l10n.dart'; +// import 'package:ente_auth/services/user_service.dart'; +// import 'package:ente_auth/theme/ente_theme.dart'; +// import 'package:ente_auth/ui/common/dynamic_fab.dart'; +// import 'package:ente_auth/utils/platform_util.dart'; +// import 'package:ente_auth/utils/toast_util.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter/services.dart'; +// import 'package:password_strength/password_strength.dart'; +// import 'package:step_progress_indicator/step_progress_indicator.dart'; +// import "package:styled_text/styled_text.dart"; -class EmailEntryPage extends StatefulWidget { - const EmailEntryPage({super.key}); +// class EmailEntryPage extends StatefulWidget { +// const EmailEntryPage({super.key}); - @override - State createState() => _EmailEntryPageState(); -} +// @override +// State createState() => _EmailEntryPageState(); +// } -class _EmailEntryPageState extends State { - static const kMildPasswordStrengthThreshold = 0.4; - static const kStrongPasswordStrengthThreshold = 0.7; +// class _EmailEntryPageState extends State { +// static const kMildPasswordStrengthThreshold = 0.4; +// static const kStrongPasswordStrengthThreshold = 0.7; - final _config = Configuration.instance; - final _passwordController1 = TextEditingController(); - final _passwordController2 = TextEditingController(); - final Color _validFieldValueColor = const Color.fromARGB(51, 157, 45, 194); +// final _config = Configuration.instance; +// final _passwordController1 = TextEditingController(); +// final _passwordController2 = TextEditingController(); +// final Color _validFieldValueColor = const Color.fromARGB(51, 157, 45, 194); - String? _email; - String? _password; - String _cnfPassword = ''; - String _referralSource = ''; - double _passwordStrength = 0.0; - bool _emailIsValid = false; - bool _hasAgreedToTOS = true; - bool _hasAgreedToE2E = false; - bool _password1Visible = false; - bool _password2Visible = false; - bool _passwordsMatch = false; +// String? _email; +// String? _password; +// String _cnfPassword = ''; +// String _referralSource = ''; +// double _passwordStrength = 0.0; +// bool _emailIsValid = false; +// bool _hasAgreedToTOS = true; +// bool _hasAgreedToE2E = false; +// bool _password1Visible = false; +// bool _password2Visible = false; +// bool _passwordsMatch = false; - final _password1FocusNode = FocusNode(); - final _password2FocusNode = FocusNode(); - bool _password1InFocus = false; - bool _password2InFocus = false; - bool _passwordIsValid = false; +// final _password1FocusNode = FocusNode(); +// final _password2FocusNode = FocusNode(); +// bool _password1InFocus = false; +// bool _password2InFocus = false; +// bool _passwordIsValid = false; - @override - void initState() { - _email = _config.getEmail(); - _password1FocusNode.addListener(() { - setState(() { - _password1InFocus = _password1FocusNode.hasFocus; - }); - }); - _password2FocusNode.addListener(() { - setState(() { - _password2InFocus = _password2FocusNode.hasFocus; - }); - }); - super.initState(); - } +// @override +// void initState() { +// _email = _config.getEmail(); +// _password1FocusNode.addListener(() { +// setState(() { +// _password1InFocus = _password1FocusNode.hasFocus; +// }); +// }); +// _password2FocusNode.addListener(() { +// setState(() { +// _password2InFocus = _password2FocusNode.hasFocus; +// }); +// }); +// super.initState(); +// } - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; +// @override +// Widget build(BuildContext context) { +// final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } +// FloatingActionButtonLocation? fabLocation() { +// if (isKeypadOpen) { +// return null; +// } else { +// return FloatingActionButtonLocation.centerFloat; +// } +// } - final appBar = AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: Material( - type: MaterialType.transparency, - child: StepProgressIndicator( - totalSteps: 4, - currentStep: 1, - selectedColor: Theme.of(context).colorScheme.alternativeColor, - roundedEdges: const Radius.circular(10), - unselectedColor: - Theme.of(context).colorScheme.stepProgressUnselectedColor, - ), - ), - ); - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: appBar, - body: _getBody(), - floatingActionButton: DynamicFAB( - isKeypadOpen: isKeypadOpen, - isFormValid: _isFormValid(), - buttonText: context.l10n.createAccount, - onPressedFunction: () { - UserService.instance.setEmail(_email!); - _config.setVolatilePassword(_passwordController1.text); - UserService.instance.setRefSource(_referralSource); - UserService.instance.sendOtt( - context, - _email!, - isCreateAccountScreen: true, - purpose: "signup", - ); - FocusScope.of(context).unfocus(); - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } +// final appBar = AppBar( +// elevation: 0, +// leading: IconButton( +// icon: const Icon(Icons.arrow_back), +// color: Theme.of(context).iconTheme.color, +// onPressed: () { +// Navigator.of(context).pop(); +// }, +// ), +// title: Material( +// type: MaterialType.transparency, +// child: StepProgressIndicator( +// totalSteps: 4, +// currentStep: 1, +// selectedColor: Theme.of(context).colorScheme.alternativeColor, +// roundedEdges: const Radius.circular(10), +// unselectedColor: +// Theme.of(context).colorScheme.stepProgressUnselectedColor, +// ), +// ), +// ); +// return Scaffold( +// resizeToAvoidBottomInset: isKeypadOpen, +// appBar: appBar, +// body: _getBody(), +// floatingActionButton: DynamicFAB( +// isKeypadOpen: isKeypadOpen, +// isFormValid: _isFormValid(), +// buttonText: context.l10n.createAccount, +// onPressedFunction: () { +// UserService.instance.setEmail(_email!); +// _config.setVolatilePassword(_passwordController1.text); +// UserService.instance.setRefSource(_referralSource); +// UserService.instance.sendOtt( +// context, +// _email!, +// isCreateAccountScreen: true, +// purpose: "signup", +// ); +// FocusScope.of(context).unfocus(); +// }, +// ), +// floatingActionButtonLocation: fabLocation(), +// floatingActionButtonAnimator: NoScalingAnimation(), +// ); +// } - Widget _getBody() { - var passwordStrengthText = context.l10n.weakStrength; - var passwordStrengthColor = Colors.redAccent; - if (_passwordStrength > kStrongPasswordStrengthThreshold) { - passwordStrengthText = context.l10n.strongStrength; - passwordStrengthColor = Colors.greenAccent; - } else if (_passwordStrength > kMildPasswordStrengthThreshold) { - passwordStrengthText = context.l10n.moderateStrength; - passwordStrengthColor = Colors.orangeAccent; - } - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: ListView( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 30, horizontal: 20), - child: Text( - context.l10n.createNewAccount, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - style: Theme.of(context).textTheme.titleMedium, - autofillHints: const [AutofillHints.email], - decoration: InputDecoration( - fillColor: _emailIsValid ? _validFieldValueColor : null, - filled: true, - hintText: context.l10n.email, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 14, - ), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _emailIsValid - ? Icon( - Icons.check, - size: 20, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - ), - onChanged: (value) { - _email = value.trim(); - if (_emailIsValid != EmailValidator.validate(_email!)) { - setState(() { - _emailIsValid = EmailValidator.validate(_email!); - }); - } - }, - autocorrect: false, - keyboardType: TextInputType.emailAddress, - //initialValue: _email, - textInputAction: TextInputAction.next, - ), - ), - const Padding(padding: EdgeInsets.all(4)), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - controller: _passwordController1, - obscureText: !_password1Visible, - enableSuggestions: true, - autofillHints: const [AutofillHints.newPassword], - decoration: InputDecoration( - fillColor: - _passwordIsValid ? _validFieldValueColor : null, - filled: true, - hintText: context.l10n.password, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 14, - ), - suffixIcon: _password1InFocus - ? IconButton( - icon: Icon( - _password1Visible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _password1Visible = !_password1Visible; - }); - }, - ) - : _passwordIsValid - ? Icon( - Icons.check, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - ), - focusNode: _password1FocusNode, - onChanged: (password) { - if (password != _password) { - setState(() { - _password = password; - _passwordStrength = - estimatePasswordStrength(password); - _passwordIsValid = _passwordStrength >= - kMildPasswordStrengthThreshold; - _passwordsMatch = _password == _cnfPassword; - }); - } - }, - onEditingComplete: () { - _password1FocusNode.unfocus(); - _password2FocusNode.requestFocus(); - TextInput.finishAutofillContext(); - }, - ), - ), - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - keyboardType: TextInputType.visiblePassword, - controller: _passwordController2, - obscureText: !_password2Visible, - autofillHints: const [AutofillHints.newPassword], - onEditingComplete: () => TextInput.finishAutofillContext(), - decoration: InputDecoration( - fillColor: _passwordsMatch && _passwordIsValid - ? _validFieldValueColor - : null, - filled: true, - hintText: context.l10n.confirmPassword, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 14, - ), - suffixIcon: _password2InFocus - ? IconButton( - icon: Icon( - _password2Visible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _password2Visible = !_password2Visible; - }); - }, - ) - : _passwordsMatch - ? Icon( - Icons.check, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - ), - focusNode: _password2FocusNode, - onChanged: (cnfPassword) { - setState(() { - _cnfPassword = cnfPassword; - if (_password != null || _password != '') { - _passwordsMatch = _password == _cnfPassword; - } - }); - }, - ), - ), - Opacity( - opacity: (_password != '') && _password1InFocus ? 1 : 0, - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 24, vertical: 8), - child: Text( - context.l10n.passwordStrength(passwordStrengthText), - style: TextStyle( - color: passwordStrengthColor, - fontWeight: FontWeight.w500, - fontSize: 12, - ), - ), - ), - ), - const SizedBox(height: 4), - Padding( - padding: - const EdgeInsets.symmetric(vertical: 0, horizontal: 20), - child: Text( - context.l10n.hearUsWhereTitle, - style: getEnteTextTheme(context).smallFaint, - ), - ), - const SizedBox(height: 4), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - style: Theme.of(context).textTheme.titleMedium, - decoration: InputDecoration( - fillColor: null, - filled: true, - contentPadding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 14, - ), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: InkWell( - onTap: () { - showToast( - context, - context.l10n.hearUsExplanation, - ); - }, - child: Icon( - Icons.info_outline_rounded, - color: getEnteColorScheme(context).strokeMuted, - ), - ), - ), - onChanged: (value) { - _referralSource = value.trim(); - }, - autocorrect: false, - keyboardType: TextInputType.text, - textInputAction: TextInputAction.next, - ), - ), - const Divider(thickness: 1), - const SizedBox(height: 12), - _getAgreement(), - const SizedBox(height: 40), - ], - ), - ), - ), - ], - ); - } +// Widget _getBody() { +// var passwordStrengthText = context.l10n.weakStrength; +// var passwordStrengthColor = Colors.redAccent; +// if (_passwordStrength > kStrongPasswordStrengthThreshold) { +// passwordStrengthText = context.l10n.strongStrength; +// passwordStrengthColor = Colors.greenAccent; +// } else if (_passwordStrength > kMildPasswordStrengthThreshold) { +// passwordStrengthText = context.l10n.moderateStrength; +// passwordStrengthColor = Colors.orangeAccent; +// } +// return Column( +// children: [ +// Expanded( +// child: AutofillGroup( +// child: ListView( +// children: [ +// Padding( +// padding: +// const EdgeInsets.symmetric(vertical: 30, horizontal: 20), +// child: Text( +// context.l10n.createNewAccount, +// style: Theme.of(context).textTheme.headlineMedium, +// ), +// ), +// Padding( +// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), +// child: TextFormField( +// style: Theme.of(context).textTheme.titleMedium, +// autofillHints: const [AutofillHints.email], +// decoration: InputDecoration( +// fillColor: _emailIsValid ? _validFieldValueColor : null, +// filled: true, +// hintText: context.l10n.email, +// contentPadding: const EdgeInsets.symmetric( +// horizontal: 16, +// vertical: 14, +// ), +// border: UnderlineInputBorder( +// borderSide: BorderSide.none, +// borderRadius: BorderRadius.circular(6), +// ), +// suffixIcon: _emailIsValid +// ? Icon( +// Icons.check, +// size: 20, +// color: Theme.of(context) +// .inputDecorationTheme +// .focusedBorder! +// .borderSide +// .color, +// ) +// : null, +// ), +// onChanged: (value) { +// _email = value.trim(); +// if (_emailIsValid != EmailValidator.validate(_email!)) { +// setState(() { +// _emailIsValid = EmailValidator.validate(_email!); +// }); +// } +// }, +// autocorrect: false, +// keyboardType: TextInputType.emailAddress, +// //initialValue: _email, +// textInputAction: TextInputAction.next, +// ), +// ), +// const Padding(padding: EdgeInsets.all(4)), +// Padding( +// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), +// child: TextFormField( +// keyboardType: TextInputType.text, +// textInputAction: TextInputAction.next, +// controller: _passwordController1, +// obscureText: !_password1Visible, +// enableSuggestions: true, +// autofillHints: const [AutofillHints.newPassword], +// decoration: InputDecoration( +// fillColor: +// _passwordIsValid ? _validFieldValueColor : null, +// filled: true, +// hintText: context.l10n.password, +// contentPadding: const EdgeInsets.symmetric( +// horizontal: 16, +// vertical: 14, +// ), +// suffixIcon: _password1InFocus +// ? IconButton( +// icon: Icon( +// _password1Visible +// ? Icons.visibility +// : Icons.visibility_off, +// color: Theme.of(context).iconTheme.color, +// size: 20, +// ), +// onPressed: () { +// setState(() { +// _password1Visible = !_password1Visible; +// }); +// }, +// ) +// : _passwordIsValid +// ? Icon( +// Icons.check, +// color: Theme.of(context) +// .inputDecorationTheme +// .focusedBorder! +// .borderSide +// .color, +// ) +// : null, +// border: UnderlineInputBorder( +// borderSide: BorderSide.none, +// borderRadius: BorderRadius.circular(6), +// ), +// ), +// focusNode: _password1FocusNode, +// onChanged: (password) { +// if (password != _password) { +// setState(() { +// _password = password; +// _passwordStrength = +// estimatePasswordStrength(password); +// _passwordIsValid = _passwordStrength >= +// kMildPasswordStrengthThreshold; +// _passwordsMatch = _password == _cnfPassword; +// }); +// } +// }, +// onEditingComplete: () { +// _password1FocusNode.unfocus(); +// _password2FocusNode.requestFocus(); +// TextInput.finishAutofillContext(); +// }, +// ), +// ), +// const SizedBox(height: 8), +// Padding( +// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), +// child: TextFormField( +// keyboardType: TextInputType.visiblePassword, +// controller: _passwordController2, +// obscureText: !_password2Visible, +// autofillHints: const [AutofillHints.newPassword], +// onEditingComplete: () => TextInput.finishAutofillContext(), +// decoration: InputDecoration( +// fillColor: _passwordsMatch && _passwordIsValid +// ? _validFieldValueColor +// : null, +// filled: true, +// hintText: context.l10n.confirmPassword, +// contentPadding: const EdgeInsets.symmetric( +// horizontal: 16, +// vertical: 14, +// ), +// suffixIcon: _password2InFocus +// ? IconButton( +// icon: Icon( +// _password2Visible +// ? Icons.visibility +// : Icons.visibility_off, +// color: Theme.of(context).iconTheme.color, +// size: 20, +// ), +// onPressed: () { +// setState(() { +// _password2Visible = !_password2Visible; +// }); +// }, +// ) +// : _passwordsMatch +// ? Icon( +// Icons.check, +// color: Theme.of(context) +// .inputDecorationTheme +// .focusedBorder! +// .borderSide +// .color, +// ) +// : null, +// border: UnderlineInputBorder( +// borderSide: BorderSide.none, +// borderRadius: BorderRadius.circular(6), +// ), +// ), +// focusNode: _password2FocusNode, +// onChanged: (cnfPassword) { +// setState(() { +// _cnfPassword = cnfPassword; +// if (_password != null || _password != '') { +// _passwordsMatch = _password == _cnfPassword; +// } +// }); +// }, +// ), +// ), +// Opacity( +// opacity: (_password != '') && _password1InFocus ? 1 : 0, +// child: Padding( +// padding: +// const EdgeInsets.symmetric(horizontal: 24, vertical: 8), +// child: Text( +// context.l10n.passwordStrength(passwordStrengthText), +// style: TextStyle( +// color: passwordStrengthColor, +// fontWeight: FontWeight.w500, +// fontSize: 12, +// ), +// ), +// ), +// ), +// const SizedBox(height: 4), +// Padding( +// padding: +// const EdgeInsets.symmetric(vertical: 0, horizontal: 20), +// child: Text( +// context.l10n.hearUsWhereTitle, +// style: getEnteTextTheme(context).smallFaint, +// ), +// ), +// const SizedBox(height: 4), +// Padding( +// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), +// child: TextFormField( +// style: Theme.of(context).textTheme.titleMedium, +// decoration: InputDecoration( +// fillColor: null, +// filled: true, +// contentPadding: const EdgeInsets.symmetric( +// horizontal: 16, +// vertical: 14, +// ), +// border: UnderlineInputBorder( +// borderSide: BorderSide.none, +// borderRadius: BorderRadius.circular(6), +// ), +// suffixIcon: InkWell( +// onTap: () { +// showToast( +// context, +// context.l10n.hearUsExplanation, +// ); +// }, +// child: Icon( +// Icons.info_outline_rounded, +// color: getEnteColorScheme(context).strokeMuted, +// ), +// ), +// ), +// onChanged: (value) { +// _referralSource = value.trim(); +// }, +// autocorrect: false, +// keyboardType: TextInputType.text, +// textInputAction: TextInputAction.next, +// ), +// ), +// const Divider(thickness: 1), +// const SizedBox(height: 12), +// _getAgreement(), +// const SizedBox(height: 40), +// ], +// ), +// ), +// ), +// ], +// ); +// } - Container _getAgreement() { - return Container( - padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), - child: Column( - children: [ - _getTOSAgreement(), - _getPasswordAgreement(), - ], - ), - ); - } +// Container _getAgreement() { +// return Container( +// padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), +// child: Column( +// children: [ +// _getTOSAgreement(), +// _getPasswordAgreement(), +// ], +// ), +// ); +// } - Widget _getTOSAgreement() { - return GestureDetector( - onTap: () { - setState(() { - _hasAgreedToTOS = !_hasAgreedToTOS; - }); - }, - behavior: HitTestBehavior.translucent, - child: Row( - children: [ - Checkbox( - value: _hasAgreedToTOS, - side: CheckboxTheme.of(context).side, - onChanged: (value) { - setState(() { - _hasAgreedToTOS = value!; - }); - }, - ), - Expanded( - child: StyledText( - text: context.l10n.signUpTerms, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 12), - tags: { - 'u-terms': StyledTextActionTag( - (String? text, Map attrs) => - PlatformUtil.openWebView( - context, - context.l10n.termsOfServicesTitle, - "https://ente.io/terms", - ), - style: const TextStyle( - decoration: TextDecoration.underline, - ), - ), - 'u-policy': StyledTextActionTag( - (String? text, Map attrs) => - PlatformUtil.openWebView( - context, - context.l10n.privacyPolicyTitle, - "https://ente.io/privacy", - ), - style: const TextStyle( - decoration: TextDecoration.underline, - ), - ), - }, - ), - ), - ], - ), - ); - } +// Widget _getTOSAgreement() { +// return GestureDetector( +// onTap: () { +// setState(() { +// _hasAgreedToTOS = !_hasAgreedToTOS; +// }); +// }, +// behavior: HitTestBehavior.translucent, +// child: Row( +// children: [ +// Checkbox( +// value: _hasAgreedToTOS, +// side: CheckboxTheme.of(context).side, +// onChanged: (value) { +// setState(() { +// _hasAgreedToTOS = value!; +// }); +// }, +// ), +// Expanded( +// child: StyledText( +// text: context.l10n.signUpTerms, +// style: Theme.of(context) +// .textTheme +// .titleMedium! +// .copyWith(fontSize: 12), +// tags: { +// 'u-terms': StyledTextActionTag( +// (String? text, Map attrs) => +// PlatformUtil.openWebView( +// context, +// context.l10n.termsOfServicesTitle, +// "https://ente.io/terms", +// ), +// style: const TextStyle( +// decoration: TextDecoration.underline, +// ), +// ), +// 'u-policy': StyledTextActionTag( +// (String? text, Map attrs) => +// PlatformUtil.openWebView( +// context, +// context.l10n.privacyPolicyTitle, +// "https://ente.io/privacy", +// ), +// style: const TextStyle( +// decoration: TextDecoration.underline, +// ), +// ), +// }, +// ), +// ), +// ], +// ), +// ); +// } - Widget _getPasswordAgreement() { - return GestureDetector( - onTap: () { - setState(() { - _hasAgreedToE2E = !_hasAgreedToE2E; - }); - }, - behavior: HitTestBehavior.translucent, - child: Row( - children: [ - Checkbox( - value: _hasAgreedToE2E, - side: CheckboxTheme.of(context).side, - onChanged: (value) { - setState(() { - _hasAgreedToE2E = value!; - }); - }, - ), - Expanded( - child: StyledText( - text: context.l10n.ackPasswordLostWarning, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 12), - tags: { - 'underline': StyledTextActionTag( - (String? text, Map attrs) => - PlatformUtil.openWebView( - context, - context.l10n.encryption, - "https://ente.io/architecture", - ), - style: const TextStyle( - decoration: TextDecoration.underline, - ), - ), - }, - ), - ), - ], - ), - ); - } +// Widget _getPasswordAgreement() { +// return GestureDetector( +// onTap: () { +// setState(() { +// _hasAgreedToE2E = !_hasAgreedToE2E; +// }); +// }, +// behavior: HitTestBehavior.translucent, +// child: Row( +// children: [ +// Checkbox( +// value: _hasAgreedToE2E, +// side: CheckboxTheme.of(context).side, +// onChanged: (value) { +// setState(() { +// _hasAgreedToE2E = value!; +// }); +// }, +// ), +// Expanded( +// child: StyledText( +// text: context.l10n.ackPasswordLostWarning, +// style: Theme.of(context) +// .textTheme +// .titleMedium! +// .copyWith(fontSize: 12), +// tags: { +// 'underline': StyledTextActionTag( +// (String? text, Map attrs) => +// PlatformUtil.openWebView( +// context, +// context.l10n.encryption, +// "https://ente.io/architecture", +// ), +// style: const TextStyle( +// decoration: TextDecoration.underline, +// ), +// ), +// }, +// ), +// ), +// ], +// ), +// ); +// } - bool _isFormValid() { - return _emailIsValid && - _passwordsMatch && - _hasAgreedToTOS && - _hasAgreedToE2E && - _passwordIsValid; - } -} +// bool _isFormValid() { +// return _emailIsValid && +// _passwordsMatch && +// _hasAgreedToTOS && +// _hasAgreedToE2E && +// _passwordIsValid; +// } +// } diff --git a/mobile/apps/auth/lib/ui/account/sessions_page.dart b/mobile/apps/auth/lib/ui/account/sessions_page.dart deleted file mode 100644 index 1815b20e23..0000000000 --- a/mobile/apps/auth/lib/ui/account/sessions_page.dart +++ /dev/null @@ -1,228 +0,0 @@ -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/sessions.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/common/loading_widget.dart'; -import 'package:ente_auth/utils/date_time_util.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; - -class SessionsPage extends StatefulWidget { - const SessionsPage({super.key}); - - @override - State createState() => _SessionsPageState(); -} - -class _SessionsPageState extends State { - Sessions? _sessions; - final Logger _logger = Logger("SessionsPageState"); - - @override - void initState() { - _fetchActiveSessions().onError((error, stackTrace) { - showToast(context, "Failed to fetch active sessions"); - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - elevation: 0, - title: Text(context.l10n.activeSessions), - ), - body: _getBody(), - ); - } - - Widget _getBody() { - if (_sessions == null) { - return const Center(child: EnteLoadingWidget()); - } - final List rows = []; - rows.add(const Padding(padding: EdgeInsets.all(4))); - for (final session in _sessions!.sessions) { - rows.add(_getSessionWidget(session)); - } - return SingleChildScrollView( - child: Column( - children: rows, - ), - ); - } - - Widget _getSessionWidget(Session session) { - final lastUsedTime = - DateTime.fromMicrosecondsSinceEpoch(session.lastUsedTime); - return Column( - children: [ - InkWell( - onTap: () async { - _showSessionTerminationDialog(session); - }, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _getUAWidget(session), - const Padding(padding: EdgeInsets.all(4)), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible( - child: Text( - session.ip, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.8), - fontSize: 14, - ), - ), - ), - const Padding(padding: EdgeInsets.all(8)), - Flexible( - child: Text( - getFormattedTime(lastUsedTime), - style: TextStyle( - color: Theme.of(context) - .colorScheme - .onSurface - .withOpacity(0.8), - fontSize: 12, - ), - ), - ), - ], - ), - ], - ), - ), - ), - const Divider(), - ], - ); - } - - Future _terminateSession(Session session) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - await UserService.instance.terminateSession(session.token); - await _fetchActiveSessions(); - await dialog.hide(); - } catch (e) { - await dialog.hide(); - _logger.severe('failed to terminate'); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.somethingWentWrongPleaseTryAgain, - ); - } - } - - Future _fetchActiveSessions() async { - _sessions = await UserService.instance.getActiveSessions().onError((e, s) { - _logger.severe("failed to fetch active sessions", e, s); - throw e!; - }); - if (_sessions != null) { - _sessions!.sessions.sort((first, second) { - return second.lastUsedTime.compareTo(first.lastUsedTime); - }); - if (mounted) { - setState(() {}); - } - } - } - - void _showSessionTerminationDialog(Session session) { - final isLoggingOutFromThisDevice = - session.token == Configuration.instance.getToken(); - Widget text; - if (isLoggingOutFromThisDevice) { - text = Text( - context.l10n.thisWillLogYouOutOfThisDevice, - ); - } else { - text = SingleChildScrollView( - child: Column( - children: [ - Text( - context.l10n.thisWillLogYouOutOfTheFollowingDevice, - ), - const Padding(padding: EdgeInsets.all(8)), - Text( - session.ua, - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ), - ); - } - final AlertDialog alert = AlertDialog( - title: Text(context.l10n.terminateSession), - content: text, - actions: [ - TextButton( - child: Text( - context.l10n.terminate, - style: const TextStyle( - color: Colors.red, - ), - ), - onPressed: () async { - Navigator.of(context, rootNavigator: true).pop('dialog'); - if (isLoggingOutFromThisDevice) { - await UserService.instance.logout(context); - } else { - await _terminateSession(session); - } - }, - ), - TextButton( - child: Text( - context.l10n.cancel, - style: TextStyle( - color: isLoggingOutFromThisDevice - ? Theme.of(context).colorScheme.alternativeColor - : Theme.of(context).colorScheme.defaultTextColor, - ), - ), - onPressed: () { - Navigator.of(context, rootNavigator: true).pop('dialog'); - }, - ), - ], - ); - - showDialog( - context: context, - builder: (BuildContext context) { - return alert; - }, - ); - } - - Widget _getUAWidget(Session session) { - if (session.token == Configuration.instance.getToken()) { - return Text( - context.l10n.thisDevice, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).colorScheme.alternativeColor, - ), - ); - } - return Text(session.prettyUA); - } -} diff --git a/mobile/apps/auth/lib/ui/common/dialogs.dart b/mobile/apps/auth/lib/ui/common/dialogs.dart index 60879335e5..4f76bcf7c1 100644 --- a/mobile/apps/auth/lib/ui/common/dialogs.dart +++ b/mobile/apps/auth/lib/ui/common/dialogs.dart @@ -1,9 +1,9 @@ -import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/dialog_widget.dart'; import 'package:ente_auth/ui/components/models/button_result.dart'; import 'package:ente_auth/ui/components/models/button_type.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/material.dart'; enum DialogUserChoice { firstChoice, secondChoice } diff --git a/mobile/apps/auth/lib/ui/components/buttons/button_widget.dart b/mobile/apps/auth/lib/ui/components/buttons/button_widget.dart index 9e589cd7a0..10abb5c39f 100644 --- a/mobile/apps/auth/lib/ui/components/buttons/button_widget.dart +++ b/mobile/apps/auth/lib/ui/components/buttons/button_widget.dart @@ -1,5 +1,4 @@ -import 'package:ente_auth/models/execution_states.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/theme/colors.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/theme/text_style.dart'; @@ -9,6 +8,7 @@ import 'package:ente_auth/ui/components/models/button_type.dart'; import 'package:ente_auth/ui/components/models/custom_button_style.dart'; import 'package:ente_auth/utils/debouncer.dart'; import "package:ente_auth/utils/dialog_util.dart"; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; diff --git a/mobile/apps/auth/lib/ui/components/dialog_widget.dart b/mobile/apps/auth/lib/ui/components/dialog_widget.dart index bebd5c9f42..8b8b417074 100644 --- a/mobile/apps/auth/lib/ui/components/dialog_widget.dart +++ b/mobile/apps/auth/lib/ui/components/dialog_widget.dart @@ -1,7 +1,6 @@ import 'dart:math'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/theme/colors.dart'; import 'package:ente_auth/theme/effects.dart'; import 'package:ente_auth/theme/ente_theme.dart'; @@ -11,6 +10,7 @@ import 'package:ente_auth/ui/components/models/button_result.dart'; import 'package:ente_auth/ui/components/models/button_type.dart'; import 'package:ente_auth/ui/components/separators.dart'; import 'package:ente_auth/ui/components/text_input_widget.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/material.dart'; ///Will return null if dismissed by tapping outside diff --git a/mobile/apps/auth/lib/ui/components/menu_item_widget.dart b/mobile/apps/auth/lib/ui/components/menu_item_widget.dart index f5d6e9dff7..ced35b51e1 100644 --- a/mobile/apps/auth/lib/ui/components/menu_item_widget.dart +++ b/mobile/apps/auth/lib/ui/components/menu_item_widget.dart @@ -1,8 +1,8 @@ -import 'package:ente_auth/models/execution_states.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/components/menu_item_child_widgets.dart'; import 'package:ente_auth/utils/debouncer.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/apps/auth/lib/ui/components/text_input_widget.dart b/mobile/apps/auth/lib/ui/components/text_input_widget.dart index 2a06d3b71a..65c8b7fd11 100644 --- a/mobile/apps/auth/lib/ui/components/text_input_widget.dart +++ b/mobile/apps/auth/lib/ui/components/text_input_widget.dart @@ -1,9 +1,9 @@ -import 'package:ente_auth/models/execution_states.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/ui/components/separators.dart'; import 'package:ente_auth/utils/debouncer.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart index 3fc0d153cc..0e558ed510 100644 --- a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart +++ b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart @@ -1,8 +1,8 @@ import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/models/execution_states.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/utils/debouncer.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/material.dart'; typedef OnChangedCallBack = void Function(bool); diff --git a/mobile/apps/auth/lib/ui/passkey_page.dart b/mobile/apps/auth/lib/ui/passkey_page.dart index 906d2122df..d4b37c615b 100644 --- a/mobile/apps/auth/lib/ui/passkey_page.dart +++ b/mobile/apps/auth/lib/ui/passkey_page.dart @@ -1,14 +1,14 @@ import 'dart:convert'; import 'package:app_links/app_links.dart'; +import 'package:ente_accounts/models/two_factor.dart'; +import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/errors.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/account/two_factor.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/models/button_type.dart'; -import 'package:ente_auth/ui/two_factor_authentication_page.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart index 89719c1da7..568fd788dc 100644 --- a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart @@ -1,9 +1,9 @@ +import 'package:ente_accounts/pages/change_email_dialog.dart'; +import 'package:ente_accounts/pages/delete_account_page.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/account/change_email_dialog.dart'; -import 'package:ente_auth/ui/account/delete_account_page.dart'; import 'package:ente_auth/ui/account/password_entry_page.dart'; import 'package:ente_auth/ui/account/recovery_key_page.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; @@ -151,8 +151,9 @@ class AccountSectionWidget extends StatelessWidget { trailingIcon: Icons.chevron_right_outlined, trailingIconIsMuted: true, onTap: () async { + final config = Configuration.instance; // ignore: unawaited_futures - routeToPage(context, const DeleteAccountPage()); + routeToPage(context, DeleteAccountPage(config)); }, ), sectionOptionSpacing, diff --git a/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart b/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart index 1c30dd2bbc..6c5bcd168d 100644 --- a/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart @@ -1,8 +1,8 @@ import 'dart:io'; +import 'package:ente_accounts/models/user_details.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/user_details.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/components/banner_widget.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart index 1053e68b41..9fa7d06b54 100644 --- a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart @@ -1,22 +1,22 @@ import 'dart:async'; import 'dart:typed_data'; +import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/pages/sessions_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/user_details.dart'; import 'package:ente_auth/services/passkey_service.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/account/request_pwd_verification_page.dart'; -import 'package:ente_auth/ui/account/sessions_page.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; import 'package:ente_auth/ui/components/menu_item_widget.dart'; import 'package:ente_auth/ui/components/models/button_result.dart'; import 'package:ente_auth/ui/components/toggle_switch_widget.dart'; -import 'package:ente_auth/ui/settings/common_settings.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; +import 'package:ente_auth/ui/settings/common_settings.dart'; +import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; @@ -128,7 +128,7 @@ class _SecuritySectionWidgetState extends State { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return const SessionsPage(); + return SessionsPage(Configuration.instance); }, ), ); diff --git a/mobile/apps/auth/lib/ui/two_factor_authentication_page.dart b/mobile/apps/auth/lib/ui/two_factor_authentication_page.dart deleted file mode 100644 index 86dfa503e9..0000000000 --- a/mobile/apps/auth/lib/ui/two_factor_authentication_page.dart +++ /dev/null @@ -1,161 +0,0 @@ -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/account/two_factor.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/lifecycle_event_handler.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:pinput/pinput.dart'; - -class TwoFactorAuthenticationPage extends StatefulWidget { - final String sessionID; - - const TwoFactorAuthenticationPage(this.sessionID, {super.key}); - - @override - State createState() => - _TwoFactorAuthenticationPageState(); -} - -class _TwoFactorAuthenticationPageState - extends State { - final _pinController = TextEditingController(); - final _pinPutDecoration = PinTheme( - height: 45, - width: 45, - decoration: BoxDecoration( - border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)), - borderRadius: BorderRadius.circular(15.0), - ), - ); - String _code = ""; - late LifecycleEventHandler _lifecycleEventHandler; - - @override - void initState() { - _lifecycleEventHandler = LifecycleEventHandler( - resumeCallBack: () async { - if (mounted) { - final data = await Clipboard.getData(Clipboard.kTextPlain); - if (data != null && data.text != null && data.text!.length == 6) { - _pinController.text = data.text!; - } - } - }, - ); - WidgetsBinding.instance.addObserver(_lifecycleEventHandler); - super.initState(); - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(_lifecycleEventHandler); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Scaffold( - appBar: AppBar( - title: Text( - l10n.twoFactorAuthTitle, - ), - ), - body: _getBody(), - ); - } - - Widget _getBody() { - final l10n = context.l10n; - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - Text( - l10n.enterCodeHint, - style: const TextStyle( - height: 1.4, - fontSize: 16, - ), - textAlign: TextAlign.center, - ), - const Padding(padding: EdgeInsets.all(32)), - Padding( - padding: const EdgeInsets.fromLTRB(40, 0, 40, 0), - child: Pinput( - length: 6, - onCompleted: (String code) { - _verifyTwoFactorCode(code); - }, - onChanged: (String pin) { - setState(() { - _code = pin; - }); - }, - controller: _pinController, - submittedPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.0), - border: Border.all( - color: const Color.fromRGBO(45, 194, 98, 0.5), - ), - ), - ), - defaultPinTheme: _pinPutDecoration, - followingPinTheme: _pinPutDecoration.copyWith( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10.0), - border: Border.all( - color: const Color.fromRGBO(45, 194, 98, 0.5), - ), - ), - ), - autofocus: true, - ), - ), - const Padding(padding: EdgeInsets.all(24)), - Container( - padding: const EdgeInsets.fromLTRB(80, 0, 80, 0), - width: double.infinity, - height: 64, - child: OutlinedButton( - onPressed: _code.length == 6 - ? () async { - await _verifyTwoFactorCode(_code); - } - : null, - child: Text(l10n.verify), - ), - ), - const Padding(padding: EdgeInsets.all(30)), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - UserService.instance.recoverTwoFactor( - context, - widget.sessionID, - TwoFactorType.totp, - ); - }, - child: Container( - padding: const EdgeInsets.all(10), - child: Center( - child: Text( - l10n.lostDeviceTitle, - style: const TextStyle( - decoration: TextDecoration.underline, - fontSize: 12, - ), - ), - ), - ), - ), - ], - ); - } - - Future _verifyTwoFactorCode(String code) async { - await UserService.instance.verifyTwoFactor(context, widget.sessionID, code); - } -} diff --git a/mobile/apps/auth/lib/ui/two_factor_recovery_page.dart b/mobile/apps/auth/lib/ui/two_factor_recovery_page.dart deleted file mode 100644 index 2f6ea02c55..0000000000 --- a/mobile/apps/auth/lib/ui/two_factor_recovery_page.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/account/two_factor.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/utils/email_util.dart'; -import 'package:flutter/material.dart'; - -class TwoFactorRecoveryPage extends StatefulWidget { - final String sessionID; - final String encryptedSecret; - final String secretDecryptionNonce; - final TwoFactorType type; - - const TwoFactorRecoveryPage( - this.type, - this.sessionID, - this.encryptedSecret, - this.secretDecryptionNonce, { - super.key, - }); - - @override - State createState() => _TwoFactorRecoveryPageState(); -} - -class _TwoFactorRecoveryPageState extends State { - final _recoveryKey = TextEditingController(); - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Scaffold( - appBar: AppBar( - title: Text( - l10n.recoverAccount, - style: const TextStyle( - fontSize: 18, - ), - ), - ), - body: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(60, 0, 60, 0), - child: TextFormField( - decoration: InputDecoration( - hintText: l10n.enterRecoveryKeyHint, - contentPadding: const EdgeInsets.all(20), - ), - style: const TextStyle( - fontSize: 14, - fontFeatures: [FontFeature.tabularFigures()], - ), - controller: _recoveryKey, - autofocus: false, - autocorrect: false, - keyboardType: TextInputType.multiline, - maxLines: null, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Padding(padding: EdgeInsets.all(24)), - Container( - padding: const EdgeInsets.fromLTRB(80, 0, 80, 0), - width: double.infinity, - height: 64, - child: OutlinedButton( - onPressed: _recoveryKey.text.isNotEmpty - ? () async { - await UserService.instance.removeTwoFactor( - context, - widget.type, - widget.sessionID, - _recoveryKey.text, - widget.encryptedSecret, - widget.secretDecryptionNonce, - ); - } - : null, - child: Text(l10n.recover), - ), - ), - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () async { - await openSupportPage(null, null); - }, - child: Container( - padding: const EdgeInsets.all(40), - child: Center( - child: Text( - l10n.noRecoveryKeyTitle, - style: TextStyle( - decoration: TextDecoration.underline, - fontSize: 12, - color: - getEnteColorScheme(context).textBase.withOpacity(0.9), - ), - ), - ), - ), - ), - ], - ), - ); - } -} diff --git a/mobile/apps/auth/lib/utils/dialog_util.dart b/mobile/apps/auth/lib/utils/dialog_util.dart index 1f64414f8f..563edf4a1c 100644 --- a/mobile/apps/auth/lib/utils/dialog_util.dart +++ b/mobile/apps/auth/lib/utils/dialog_util.dart @@ -3,8 +3,7 @@ import 'dart:math'; import 'package:confetti/confetti.dart'; import "package:dio/dio.dart"; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/typedefs.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/theme/colors.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/ui/common/progress_dialog.dart'; @@ -16,6 +15,7 @@ import 'package:ente_auth/ui/components/models/button_result.dart'; import 'package:ente_auth/ui/components/models/button_type.dart'; import 'package:ente_auth/utils/email_util.dart'; import 'package:ente_auth/utils/platform_util.dart'; +import 'package:ente_base/typedefs.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 1dd9150ce5..bca81b2cd0 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -337,19 +337,21 @@ packages: flutter_inappwebview: dependency: transitive description: - name: flutter_inappwebview - sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" - url: "https://pub.dev" - source: hosted - version: "6.1.5" + path: flutter_inappwebview + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "6.2.0-beta.3" flutter_inappwebview_android: dependency: transitive description: - name: flutter_inappwebview_android - sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" - url: "https://pub.dev" - source: hosted - version: "1.1.3" + path: flutter_inappwebview_android + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -361,43 +363,48 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - name: flutter_inappwebview_ios - sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_ios + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_macos: dependency: transitive description: - name: flutter_inappwebview_macos - sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_macos + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_platform_interface: dependency: transitive description: - name: flutter_inappwebview_platform_interface - sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 - url: "https://pub.dev" - source: hosted - version: "1.3.0+1" + path: flutter_inappwebview_platform_interface + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.4.0-beta.3" flutter_inappwebview_web: dependency: transitive description: - name: flutter_inappwebview_web - sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" - url: "https://pub.dev" - source: hosted - version: "1.1.2" + path: flutter_inappwebview_web + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "1.2.0-beta.3" flutter_inappwebview_windows: dependency: transitive description: - name: flutter_inappwebview_windows - sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" - url: "https://pub.dev" - source: hosted - version: "0.6.0" + path: flutter_inappwebview_windows + ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" + url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" + source: git + version: "0.7.0-beta.3" flutter_lints: dependency: "direct dev" description: From d9d9acfa3ef35280d2d0ef6d17ebcc7d796985fb Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:26:20 +0530 Subject: [PATCH 108/164] Refractor accounts section to use common code from `packages/accounts` --- mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart index 0e558ed510..ecdb19f29b 100644 --- a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart +++ b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart @@ -1,5 +1,6 @@ import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/models/execution_states.dart'; +import 'package:ente_auth/theme/colors.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/utils/debouncer.dart'; import 'package:ente_base/typedefs.dart'; From ae1a43b8bf60caca8080aba453b74794b746ae3a Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:26:20 +0530 Subject: [PATCH 109/164] Refractor accounts section to use common code from `packages/accounts` From b695db80abdc591ab43955b55214172b2be1f1fa Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:47:06 +0530 Subject: [PATCH 110/164] Refactor HomePage to extend BaseHomePage for improved structure --- mobile/apps/auth/lib/ui/home_page.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/apps/auth/lib/ui/home_page.dart b/mobile/apps/auth/lib/ui/home_page.dart index af1deec96f..f6874882d7 100644 --- a/mobile/apps/auth/lib/ui/home_page.dart +++ b/mobile/apps/auth/lib/ui/home_page.dart @@ -39,6 +39,7 @@ import 'package:ente_auth/utils/totp_util.dart'; import 'package:ente_events/event_bus.dart'; import 'package:ente_lock_screen/lock_screen_settings.dart'; import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_ui/pages/base_home_page.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -47,7 +48,7 @@ import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:logging/logging.dart'; import 'package:move_to_background/move_to_background.dart'; -class HomePage extends StatefulWidget { +class HomePage extends BaseHomePage { const HomePage({super.key}); @override From 57569e79fe036df9bbf1326d20849f9d66c7b9c9 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:47:16 +0530 Subject: [PATCH 111/164] Add ente_ui dependency to pubspec.yaml and update its lock status in pubspec.lock --- mobile/apps/auth/pubspec.lock | 2 +- mobile/apps/auth/pubspec.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 3954c018fe..8644a30981 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -464,7 +464,7 @@ packages: source: path version: "1.0.0" ente_ui: - dependency: transitive + dependency: "direct main" description: path: "../../packages/ui" relative: true diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 785ab8017b..48f946d3f6 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: path: ../../packages/logging ente_network: path: ../../packages/network + ente_ui: + path: ../../packages/ui event_bus: ^2.0.0 expandable: ^5.0.1 expansion_tile_card: ^3.0.0 From 6f94d91afb62bc25c21b819e288edb101f88c817 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:48:04 +0530 Subject: [PATCH 112/164] More code refractor in auth/accounts section --- .../lib/onboarding/view/onboarding_page.dart | 18 +- .../apps/auth/lib/services/user_service.dart | 24 +- .../auth/lib/ui/account/email_entry_page.dart | 516 ------------------ .../apps/auth/lib/ui/account/login_page.dart | 227 -------- .../account/login_pwd_verification_page.dart | 345 ------------ .../lib/ui/account/ott_verification_page.dart | 223 -------- .../lib/ui/account/password_entry_page.dart | 515 ----------------- .../auth/lib/ui/account/recovery_page.dart | 9 +- mobile/apps/auth/lib/ui/passkey_page.dart | 235 -------- .../ui/settings/account_section_widget.dart | 9 +- 10 files changed, 37 insertions(+), 2084 deletions(-) delete mode 100644 mobile/apps/auth/lib/ui/account/email_entry_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/login_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/login_pwd_verification_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/ott_verification_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/password_entry_page.dart delete mode 100644 mobile/apps/auth/lib/ui/passkey_page.dart diff --git a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart index 9037cf2652..c9fcd645f5 100644 --- a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart +++ b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart @@ -2,6 +2,8 @@ import 'dart:async'; import 'dart:io'; import 'package:ente_accounts/pages/email_entry_page.dart'; +import 'package:ente_accounts/pages/login_page.dart'; +import 'package:ente_accounts/pages/password_entry_page.dart'; import 'package:ente_auth/app/view/app.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; @@ -9,9 +11,7 @@ import 'package:ente_auth/events/trigger_logout_event.dart'; import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/theme/text_style.dart'; -import 'package:ente_auth/ui/account/login_page.dart'; import 'package:ente_auth/ui/account/logout_dialog.dart'; -import 'package:ente_auth/ui/account/password_entry_page.dart'; import 'package:ente_auth/ui/account/password_reentry_page.dart'; import 'package:ente_auth/ui/common/gradient_button.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; @@ -265,8 +265,10 @@ class _OnboardingPageState extends State { // No key if (Configuration.instance.getKeyAttributes() == null) { // Never had a key - page = const PasswordEntryPage( - mode: PasswordEntryMode.set, + page = PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.set, + const HomePage(), ); } else if (Configuration.instance.getKey() == null) { // Yet to decrypt the key @@ -288,13 +290,15 @@ class _OnboardingPageState extends State { void _navigateToSignInPage() { Widget page; if (Configuration.instance.getEncryptedToken() == null) { - page = const LoginPage(); + page = LoginPage(Configuration.instance); } else { // No key if (Configuration.instance.getKeyAttributes() == null) { // Never had a key - page = const PasswordEntryPage( - mode: PasswordEntryMode.set, + page = PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.set, + const HomePage(), ); } else if (Configuration.instance.getKey() == null) { // Yet to decrypt the key diff --git a/mobile/apps/auth/lib/services/user_service.dart b/mobile/apps/auth/lib/services/user_service.dart index c79016b3d4..cb676ad237 100644 --- a/mobile/apps/auth/lib/services/user_service.dart +++ b/mobile/apps/auth/lib/services/user_service.dart @@ -10,21 +10,21 @@ import 'package:ente_accounts/models/set_keys_request.dart'; import 'package:ente_accounts/models/set_recovery_key_request.dart'; import 'package:ente_accounts/models/two_factor.dart'; import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/pages/login_page.dart'; +import 'package:ente_accounts/pages/ott_verification_page.dart'; +import 'package:ente_accounts/pages/passkey_page.dart'; +import 'package:ente_accounts/pages/password_entry_page.dart'; import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/core/errors.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/api/user/srp.dart'; -import 'package:ente_auth/ui/account/login_page.dart'; -import 'package:ente_auth/ui/account/ott_verification_page.dart'; -import 'package:ente_auth/ui/account/password_entry_page.dart'; +import 'package:ente_auth/models/api/user/srp.dart'; import 'package:ente_auth/ui/account/password_reentry_page.dart'; import 'package:ente_auth/ui/account/recovery_page.dart'; import 'package:ente_auth/ui/common/progress_dialog.dart'; import 'package:ente_auth/ui/home_page.dart'; -import 'package:ente_auth/ui/passkey_page.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:ente_base/models/key_attributes.dart'; @@ -405,6 +405,7 @@ class UserService { } if (passkeySessionID.isNotEmpty) { page = PasskeyPage( + Configuration.instance, passkeySessionID, totp2FASessionID: twoFASessionID, accountsUrl: accountsUrl, @@ -420,8 +421,10 @@ class UserService { page = const PasswordReentryPage(); } } else { - page = const PasswordEntryPage( - mode: PasswordEntryMode.set, + page = PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.set, + const HomePage(), ); } } @@ -721,6 +724,7 @@ class UserService { Configuration.instance.setVolatilePassword(userPassword); if (passkeySessionID.isNotEmpty) { page = PasskeyPage( + Configuration.instance, passkeySessionID, totp2FASessionID: twoFASessionID, accountsUrl: accountsUrl, @@ -838,7 +842,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return const LoginPage(); + return LoginPage(Configuration.instance); }, ), (route) => route.isFirst, @@ -904,7 +908,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return const LoginPage(); + return LoginPage(Configuration.instance); }, ), (route) => route.isFirst, @@ -1002,7 +1006,7 @@ class UserService { Navigator.of(context).pushAndRemoveUntil( MaterialPageRoute( builder: (BuildContext context) { - return const LoginPage(); + return LoginPage(Configuration.instance); }, ), (route) => route.isFirst, diff --git a/mobile/apps/auth/lib/ui/account/email_entry_page.dart b/mobile/apps/auth/lib/ui/account/email_entry_page.dart deleted file mode 100644 index 24cc77cbce..0000000000 --- a/mobile/apps/auth/lib/ui/account/email_entry_page.dart +++ /dev/null @@ -1,516 +0,0 @@ -// import 'package:email_validator/email_validator.dart'; -// import 'package:ente_auth/core/configuration.dart'; -// import 'package:ente_auth/ente_theme_data.dart'; -// import 'package:ente_auth/l10n/l10n.dart'; -// import 'package:ente_auth/services/user_service.dart'; -// import 'package:ente_auth/theme/ente_theme.dart'; -// import 'package:ente_auth/ui/common/dynamic_fab.dart'; -// import 'package:ente_auth/utils/platform_util.dart'; -// import 'package:ente_auth/utils/toast_util.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter/services.dart'; -// import 'package:password_strength/password_strength.dart'; -// import 'package:step_progress_indicator/step_progress_indicator.dart'; -// import "package:styled_text/styled_text.dart"; - -// class EmailEntryPage extends StatefulWidget { -// const EmailEntryPage({super.key}); - -// @override -// State createState() => _EmailEntryPageState(); -// } - -// class _EmailEntryPageState extends State { -// static const kMildPasswordStrengthThreshold = 0.4; -// static const kStrongPasswordStrengthThreshold = 0.7; - -// final _config = Configuration.instance; -// final _passwordController1 = TextEditingController(); -// final _passwordController2 = TextEditingController(); -// final Color _validFieldValueColor = const Color.fromARGB(51, 157, 45, 194); - -// String? _email; -// String? _password; -// String _cnfPassword = ''; -// String _referralSource = ''; -// double _passwordStrength = 0.0; -// bool _emailIsValid = false; -// bool _hasAgreedToTOS = true; -// bool _hasAgreedToE2E = false; -// bool _password1Visible = false; -// bool _password2Visible = false; -// bool _passwordsMatch = false; - -// final _password1FocusNode = FocusNode(); -// final _password2FocusNode = FocusNode(); -// bool _password1InFocus = false; -// bool _password2InFocus = false; -// bool _passwordIsValid = false; - -// @override -// void initState() { -// _email = _config.getEmail(); -// _password1FocusNode.addListener(() { -// setState(() { -// _password1InFocus = _password1FocusNode.hasFocus; -// }); -// }); -// _password2FocusNode.addListener(() { -// setState(() { -// _password2InFocus = _password2FocusNode.hasFocus; -// }); -// }); -// super.initState(); -// } - -// @override -// Widget build(BuildContext context) { -// final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - -// FloatingActionButtonLocation? fabLocation() { -// if (isKeypadOpen) { -// return null; -// } else { -// return FloatingActionButtonLocation.centerFloat; -// } -// } - -// final appBar = AppBar( -// elevation: 0, -// leading: IconButton( -// icon: const Icon(Icons.arrow_back), -// color: Theme.of(context).iconTheme.color, -// onPressed: () { -// Navigator.of(context).pop(); -// }, -// ), -// title: Material( -// type: MaterialType.transparency, -// child: StepProgressIndicator( -// totalSteps: 4, -// currentStep: 1, -// selectedColor: Theme.of(context).colorScheme.alternativeColor, -// roundedEdges: const Radius.circular(10), -// unselectedColor: -// Theme.of(context).colorScheme.stepProgressUnselectedColor, -// ), -// ), -// ); -// return Scaffold( -// resizeToAvoidBottomInset: isKeypadOpen, -// appBar: appBar, -// body: _getBody(), -// floatingActionButton: DynamicFAB( -// isKeypadOpen: isKeypadOpen, -// isFormValid: _isFormValid(), -// buttonText: context.l10n.createAccount, -// onPressedFunction: () { -// UserService.instance.setEmail(_email!); -// _config.setVolatilePassword(_passwordController1.text); -// UserService.instance.setRefSource(_referralSource); -// UserService.instance.sendOtt( -// context, -// _email!, -// isCreateAccountScreen: true, -// purpose: "signup", -// ); -// FocusScope.of(context).unfocus(); -// }, -// ), -// floatingActionButtonLocation: fabLocation(), -// floatingActionButtonAnimator: NoScalingAnimation(), -// ); -// } - -// Widget _getBody() { -// var passwordStrengthText = context.l10n.weakStrength; -// var passwordStrengthColor = Colors.redAccent; -// if (_passwordStrength > kStrongPasswordStrengthThreshold) { -// passwordStrengthText = context.l10n.strongStrength; -// passwordStrengthColor = Colors.greenAccent; -// } else if (_passwordStrength > kMildPasswordStrengthThreshold) { -// passwordStrengthText = context.l10n.moderateStrength; -// passwordStrengthColor = Colors.orangeAccent; -// } -// return Column( -// children: [ -// Expanded( -// child: AutofillGroup( -// child: ListView( -// children: [ -// Padding( -// padding: -// const EdgeInsets.symmetric(vertical: 30, horizontal: 20), -// child: Text( -// context.l10n.createNewAccount, -// style: Theme.of(context).textTheme.headlineMedium, -// ), -// ), -// Padding( -// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), -// child: TextFormField( -// style: Theme.of(context).textTheme.titleMedium, -// autofillHints: const [AutofillHints.email], -// decoration: InputDecoration( -// fillColor: _emailIsValid ? _validFieldValueColor : null, -// filled: true, -// hintText: context.l10n.email, -// contentPadding: const EdgeInsets.symmetric( -// horizontal: 16, -// vertical: 14, -// ), -// border: UnderlineInputBorder( -// borderSide: BorderSide.none, -// borderRadius: BorderRadius.circular(6), -// ), -// suffixIcon: _emailIsValid -// ? Icon( -// Icons.check, -// size: 20, -// color: Theme.of(context) -// .inputDecorationTheme -// .focusedBorder! -// .borderSide -// .color, -// ) -// : null, -// ), -// onChanged: (value) { -// _email = value.trim(); -// if (_emailIsValid != EmailValidator.validate(_email!)) { -// setState(() { -// _emailIsValid = EmailValidator.validate(_email!); -// }); -// } -// }, -// autocorrect: false, -// keyboardType: TextInputType.emailAddress, -// //initialValue: _email, -// textInputAction: TextInputAction.next, -// ), -// ), -// const Padding(padding: EdgeInsets.all(4)), -// Padding( -// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), -// child: TextFormField( -// keyboardType: TextInputType.text, -// textInputAction: TextInputAction.next, -// controller: _passwordController1, -// obscureText: !_password1Visible, -// enableSuggestions: true, -// autofillHints: const [AutofillHints.newPassword], -// decoration: InputDecoration( -// fillColor: -// _passwordIsValid ? _validFieldValueColor : null, -// filled: true, -// hintText: context.l10n.password, -// contentPadding: const EdgeInsets.symmetric( -// horizontal: 16, -// vertical: 14, -// ), -// suffixIcon: _password1InFocus -// ? IconButton( -// icon: Icon( -// _password1Visible -// ? Icons.visibility -// : Icons.visibility_off, -// color: Theme.of(context).iconTheme.color, -// size: 20, -// ), -// onPressed: () { -// setState(() { -// _password1Visible = !_password1Visible; -// }); -// }, -// ) -// : _passwordIsValid -// ? Icon( -// Icons.check, -// color: Theme.of(context) -// .inputDecorationTheme -// .focusedBorder! -// .borderSide -// .color, -// ) -// : null, -// border: UnderlineInputBorder( -// borderSide: BorderSide.none, -// borderRadius: BorderRadius.circular(6), -// ), -// ), -// focusNode: _password1FocusNode, -// onChanged: (password) { -// if (password != _password) { -// setState(() { -// _password = password; -// _passwordStrength = -// estimatePasswordStrength(password); -// _passwordIsValid = _passwordStrength >= -// kMildPasswordStrengthThreshold; -// _passwordsMatch = _password == _cnfPassword; -// }); -// } -// }, -// onEditingComplete: () { -// _password1FocusNode.unfocus(); -// _password2FocusNode.requestFocus(); -// TextInput.finishAutofillContext(); -// }, -// ), -// ), -// const SizedBox(height: 8), -// Padding( -// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), -// child: TextFormField( -// keyboardType: TextInputType.visiblePassword, -// controller: _passwordController2, -// obscureText: !_password2Visible, -// autofillHints: const [AutofillHints.newPassword], -// onEditingComplete: () => TextInput.finishAutofillContext(), -// decoration: InputDecoration( -// fillColor: _passwordsMatch && _passwordIsValid -// ? _validFieldValueColor -// : null, -// filled: true, -// hintText: context.l10n.confirmPassword, -// contentPadding: const EdgeInsets.symmetric( -// horizontal: 16, -// vertical: 14, -// ), -// suffixIcon: _password2InFocus -// ? IconButton( -// icon: Icon( -// _password2Visible -// ? Icons.visibility -// : Icons.visibility_off, -// color: Theme.of(context).iconTheme.color, -// size: 20, -// ), -// onPressed: () { -// setState(() { -// _password2Visible = !_password2Visible; -// }); -// }, -// ) -// : _passwordsMatch -// ? Icon( -// Icons.check, -// color: Theme.of(context) -// .inputDecorationTheme -// .focusedBorder! -// .borderSide -// .color, -// ) -// : null, -// border: UnderlineInputBorder( -// borderSide: BorderSide.none, -// borderRadius: BorderRadius.circular(6), -// ), -// ), -// focusNode: _password2FocusNode, -// onChanged: (cnfPassword) { -// setState(() { -// _cnfPassword = cnfPassword; -// if (_password != null || _password != '') { -// _passwordsMatch = _password == _cnfPassword; -// } -// }); -// }, -// ), -// ), -// Opacity( -// opacity: (_password != '') && _password1InFocus ? 1 : 0, -// child: Padding( -// padding: -// const EdgeInsets.symmetric(horizontal: 24, vertical: 8), -// child: Text( -// context.l10n.passwordStrength(passwordStrengthText), -// style: TextStyle( -// color: passwordStrengthColor, -// fontWeight: FontWeight.w500, -// fontSize: 12, -// ), -// ), -// ), -// ), -// const SizedBox(height: 4), -// Padding( -// padding: -// const EdgeInsets.symmetric(vertical: 0, horizontal: 20), -// child: Text( -// context.l10n.hearUsWhereTitle, -// style: getEnteTextTheme(context).smallFaint, -// ), -// ), -// const SizedBox(height: 4), -// Padding( -// padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), -// child: TextFormField( -// style: Theme.of(context).textTheme.titleMedium, -// decoration: InputDecoration( -// fillColor: null, -// filled: true, -// contentPadding: const EdgeInsets.symmetric( -// horizontal: 16, -// vertical: 14, -// ), -// border: UnderlineInputBorder( -// borderSide: BorderSide.none, -// borderRadius: BorderRadius.circular(6), -// ), -// suffixIcon: InkWell( -// onTap: () { -// showToast( -// context, -// context.l10n.hearUsExplanation, -// ); -// }, -// child: Icon( -// Icons.info_outline_rounded, -// color: getEnteColorScheme(context).strokeMuted, -// ), -// ), -// ), -// onChanged: (value) { -// _referralSource = value.trim(); -// }, -// autocorrect: false, -// keyboardType: TextInputType.text, -// textInputAction: TextInputAction.next, -// ), -// ), -// const Divider(thickness: 1), -// const SizedBox(height: 12), -// _getAgreement(), -// const SizedBox(height: 40), -// ], -// ), -// ), -// ), -// ], -// ); -// } - -// Container _getAgreement() { -// return Container( -// padding: const EdgeInsets.only(left: 20, right: 20, bottom: 20), -// child: Column( -// children: [ -// _getTOSAgreement(), -// _getPasswordAgreement(), -// ], -// ), -// ); -// } - -// Widget _getTOSAgreement() { -// return GestureDetector( -// onTap: () { -// setState(() { -// _hasAgreedToTOS = !_hasAgreedToTOS; -// }); -// }, -// behavior: HitTestBehavior.translucent, -// child: Row( -// children: [ -// Checkbox( -// value: _hasAgreedToTOS, -// side: CheckboxTheme.of(context).side, -// onChanged: (value) { -// setState(() { -// _hasAgreedToTOS = value!; -// }); -// }, -// ), -// Expanded( -// child: StyledText( -// text: context.l10n.signUpTerms, -// style: Theme.of(context) -// .textTheme -// .titleMedium! -// .copyWith(fontSize: 12), -// tags: { -// 'u-terms': StyledTextActionTag( -// (String? text, Map attrs) => -// PlatformUtil.openWebView( -// context, -// context.l10n.termsOfServicesTitle, -// "https://ente.io/terms", -// ), -// style: const TextStyle( -// decoration: TextDecoration.underline, -// ), -// ), -// 'u-policy': StyledTextActionTag( -// (String? text, Map attrs) => -// PlatformUtil.openWebView( -// context, -// context.l10n.privacyPolicyTitle, -// "https://ente.io/privacy", -// ), -// style: const TextStyle( -// decoration: TextDecoration.underline, -// ), -// ), -// }, -// ), -// ), -// ], -// ), -// ); -// } - -// Widget _getPasswordAgreement() { -// return GestureDetector( -// onTap: () { -// setState(() { -// _hasAgreedToE2E = !_hasAgreedToE2E; -// }); -// }, -// behavior: HitTestBehavior.translucent, -// child: Row( -// children: [ -// Checkbox( -// value: _hasAgreedToE2E, -// side: CheckboxTheme.of(context).side, -// onChanged: (value) { -// setState(() { -// _hasAgreedToE2E = value!; -// }); -// }, -// ), -// Expanded( -// child: StyledText( -// text: context.l10n.ackPasswordLostWarning, -// style: Theme.of(context) -// .textTheme -// .titleMedium! -// .copyWith(fontSize: 12), -// tags: { -// 'underline': StyledTextActionTag( -// (String? text, Map attrs) => -// PlatformUtil.openWebView( -// context, -// context.l10n.encryption, -// "https://ente.io/architecture", -// ), -// style: const TextStyle( -// decoration: TextDecoration.underline, -// ), -// ), -// }, -// ), -// ), -// ], -// ), -// ); -// } - -// bool _isFormValid() { -// return _emailIsValid && -// _passwordsMatch && -// _hasAgreedToTOS && -// _hasAgreedToE2E && -// _passwordIsValid; -// } -// } diff --git a/mobile/apps/auth/lib/ui/account/login_page.dart b/mobile/apps/auth/lib/ui/account/login_page.dart deleted file mode 100644 index fef05707ac..0000000000 --- a/mobile/apps/auth/lib/ui/account/login_page.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'package:email_validator/email_validator.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/errors.dart'; -import "package:ente_auth/l10n/l10n.dart"; -import 'package:ente_auth/models/api/user/srp.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/account/login_pwd_verification_page.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; -import "package:styled_text/styled_text.dart"; - -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); - - @override - State createState() => _LoginPageState(); -} - -class _LoginPageState extends State { - final _config = Configuration.instance; - bool _emailIsValid = false; - String? _email; - Color? _emailInputFieldColor; - final Logger _logger = Logger('_LoginPageState'); - - Future onPressed() async { - await UserService.instance.setEmail(_email!); - Configuration.instance.resetVolatilePassword(); - SrpAttributes? attr; - bool isEmailVerificationEnabled = true; - try { - attr = await UserService.instance.getSrpAttributes(_email!); - isEmailVerificationEnabled = attr.isEmailMFAEnabled; - } catch (e) { - if (e is! SrpSetupNotCompleteError) { - _logger.severe('Error getting SRP attributes', e); - } - } - if (attr != null && !isEmailVerificationEnabled) { - await Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return LoginPasswordVerificationPage( - srpAttributes: attr!, - ); - }, - ), - ); - } else { - await UserService.instance.sendOtt( - context, - _email!, - isCreateAccountScreen: false, - purpose: 'login', - ); - } - FocusScope.of(context).unfocus(); - } - - @override - void initState() { - _email = _config.getEmail(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - body: _getBody(), - floatingActionButton: DynamicFAB( - isKeypadOpen: isKeypadOpen, - isFormValid: _emailIsValid, - buttonText: context.l10n.logInLabel, - onPressedFunction: onPressed, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Widget _getBody() { - final l10n = context.l10n; - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: ListView( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 30, horizontal: 20), - child: Text( - l10n.welcomeBack, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: TextFormField( - autofillHints: const [AutofillHints.email], - onFieldSubmitted: - _emailIsValid ? (value) => onPressed() : null, - decoration: InputDecoration( - fillColor: _emailInputFieldColor, - filled: true, - hintText: l10n.email, - contentPadding: const EdgeInsets.symmetric( - horizontal: 15, - vertical: 15, - ), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _emailIsValid - ? Icon( - Icons.check, - size: 20, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - ), - onChanged: (value) { - setState(() { - _email = value.trim(); - _emailIsValid = EmailValidator.validate(_email!); - if (_emailIsValid) { - _emailInputFieldColor = - const Color.fromARGB(51, 157, 45, 194); - } else { - _emailInputFieldColor = null; - } - }); - }, - autocorrect: false, - keyboardType: TextInputType.emailAddress, - //initialValue: _email, - autofocus: true, - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 18), - child: Divider( - thickness: 1, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - children: [ - Expanded( - flex: 5, - child: StyledText( - text: context.l10n.loginTerms, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 12), - tags: { - 'u-terms': StyledTextActionTag( - (String? text, Map attrs) => - PlatformUtil.openWebView( - context, - context.l10n.termsOfServicesTitle, - "https://ente.io/terms", - ), - style: const TextStyle( - decoration: TextDecoration.underline, - ), - ), - 'u-policy': StyledTextActionTag( - (String? text, Map attrs) => - PlatformUtil.openWebView( - context, - context.l10n.privacyPolicyTitle, - "https://ente.io/privacy", - ), - style: const TextStyle( - decoration: TextDecoration.underline, - ), - ), - }, - ), - ), - Expanded( - flex: 2, - child: Container(), - ), - ], - ), - ), - ], - ), - ), - ), - const Padding(padding: EdgeInsets.all(8)), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/login_pwd_verification_page.dart b/mobile/apps/auth/lib/ui/account/login_pwd_verification_page.dart deleted file mode 100644 index 6d43988e2c..0000000000 --- a/mobile/apps/auth/lib/ui/account/login_pwd_verification_page.dart +++ /dev/null @@ -1,345 +0,0 @@ -import "package:dio/dio.dart"; -import 'package:ente_auth/core/configuration.dart'; -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/models/api/user/srp.dart"; -import "package:ente_auth/services/user_service.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import "package:ente_auth/ui/components/buttons/button_widget.dart"; -import "package:ente_auth/utils/dialog_util.dart"; -import "package:ente_auth/utils/email_util.dart"; -import "package:ente_crypto_dart/ente_crypto_dart.dart"; -import 'package:flutter/material.dart'; -import "package:logging/logging.dart"; - -// LoginPasswordVerificationPage is a page that allows the user to enter their password to verify their identity. -// If the password is correct, then the user is either directed to -// PasswordReentryPage (if the user has not yet set up 2FA) or TwoFactorAuthenticationPage (if the user has set up 2FA). -// In the PasswordReentryPage, the password is auto-filled based on the -// volatile password. -class LoginPasswordVerificationPage extends StatefulWidget { - final SrpAttributes srpAttributes; - const LoginPasswordVerificationPage({super.key, required this.srpAttributes}); - - @override - State createState() => - _LoginPasswordVerificationPageState(); -} - -class _LoginPasswordVerificationPageState - extends State { - final _logger = Logger((_LoginPasswordVerificationPageState).toString()); - final _passwordController = TextEditingController(); - final FocusNode _passwordFocusNode = FocusNode(); - String? email; - bool _passwordInFocus = false; - bool _passwordVisible = false; - - Future onPressed() async { - FocusScope.of(context).unfocus(); - await verifyPassword(context, _passwordController.text); - } - - @override - void initState() { - super.initState(); - email = Configuration.instance.getEmail(); - _passwordFocusNode.addListener(() { - setState(() { - _passwordInFocus = _passwordFocusNode.hasFocus; - }); - }); - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - body: _getBody(), - floatingActionButton: DynamicFAB( - key: const ValueKey("verifyPasswordButton"), - isKeypadOpen: isKeypadOpen, - isFormValid: _passwordController.text.isNotEmpty, - buttonText: context.l10n.logInLabel, - onPressedFunction: onPressed, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Future verifyPassword(BuildContext context, String password) async { - final dialog = createProgressDialog( - context, - context.l10n.pleaseWait, - isDismissible: true, - ); - await dialog.show(); - try { - await UserService.instance.verifyEmailViaPassword( - context, - widget.srpAttributes, - password, - dialog, - ); - } on DioException catch (e, s) { - await dialog.hide(); - if (e.response != null && e.response!.statusCode == 401) { - _logger.severe('server reject, failed verify SRP login', e, s); - await _showContactSupportDialog( - context, - context.l10n.incorrectPasswordTitle, - context.l10n.pleaseTryAgain, - ); - } else { - _logger.severe('API failure during SRP login', e, s); - if (e.type == DioExceptionType.connectionError) { - await _showContactSupportDialog( - context, - context.l10n.noInternetConnection, - context.l10n.pleaseCheckYourInternetConnectionAndTryAgain, - ); - } else { - await _showContactSupportDialog( - context, - context.l10n.oops, - context.l10n.verificationFailedPleaseTryAgain, - ); - } - } - } catch (e, s) { - _logger.info('error during loginViaPassword', e); - await dialog.hide(); - if (e is LoginKeyDerivationError) { - _logger.severe('loginKey derivation error', e, s); - // LoginKey err, perform regular login via ott verification - await UserService.instance.sendOtt( - context, - email!, - isCreateAccountScreen: true, - ); - return; - } else if (e is KeyDerivationError) { - // device is not powerful enough to perform derive key - final dialogChoice = await showChoiceDialog( - context, - title: context.l10n.recreatePasswordTitle, - body: context.l10n.recreatePasswordBody, - firstButtonLabel: context.l10n.useRecoveryKey, - ); - if (dialogChoice!.action == ButtonAction.first) { - await UserService.instance.sendOtt( - context, - email!, - isResetPasswordScreen: true, - ); - } - return; - } else { - _logger.severe('unexpected error while verifying password', e, s); - await _showContactSupportDialog( - context, - context.l10n.oops, - context.l10n.verificationFailedPleaseTryAgain, - ); - } - } - } - - Future _showContactSupportDialog( - BuildContext context, - String title, - String message, - ) async { - final dialogChoice = await showChoiceDialog( - context, - title: title, - body: message, - firstButtonLabel: context.l10n.contactSupport, - secondButtonLabel: context.l10n.ok, - ); - if (dialogChoice!.action == ButtonAction.first) { - await sendLogs( - context, - context.l10n.contactSupport, - postShare: () {}, - ); - } - } - - Widget _getBody() { - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.only(top: 30, left: 20, right: 20), - child: Text( - context.l10n.enterPassword, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.only( - bottom: 30, - left: 22, - right: 20, - ), - child: Text( - email ?? '', - style: getEnteTextTheme(context).smallMuted, - ), - ), - Visibility( - // hidden textForm for suggesting auto-fill service for saving - // password - visible: false, - child: TextFormField( - autofillHints: const [ - AutofillHints.email, - ], - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: email, - textInputAction: TextInputAction.next, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: TextFormField( - onFieldSubmitted: _passwordController.text.isNotEmpty - ? (_) => onPressed() - : null, - key: const ValueKey("passwordInputField"), - autofillHints: const [AutofillHints.password], - decoration: InputDecoration( - hintText: context.l10n.enterYourPasswordHint, - filled: true, - contentPadding: const EdgeInsets.all(20), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _passwordInFocus - ? IconButton( - icon: Icon( - _passwordVisible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _passwordVisible = !_passwordVisible; - }); - }, - ) - : null, - ), - style: const TextStyle( - fontSize: 14, - ), - controller: _passwordController, - autofocus: true, - autocorrect: false, - obscureText: !_passwordVisible, - keyboardType: TextInputType.visiblePassword, - focusNode: _passwordFocusNode, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 18), - child: Divider( - thickness: 1, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - await UserService.instance.sendOtt( - context, - email!, - isResetPasswordScreen: true, - ); - }, - child: Center( - child: Text( - context.l10n.forgotPassword, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - final dialog = createProgressDialog( - context, - context.l10n.pleaseWait, - ); - await dialog.show(); - await Configuration.instance.logout(); - await dialog.hide(); - Navigator.of(context) - .popUntil((route) => route.isFirst); - }, - child: Center( - child: Text( - context.l10n.changeEmail, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - ], - ), - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/ott_verification_page.dart b/mobile/apps/auth/lib/ui/account/ott_verification_page.dart deleted file mode 100644 index cc9661defc..0000000000 --- a/mobile/apps/auth/lib/ui/account/ott_verification_page.dart +++ /dev/null @@ -1,223 +0,0 @@ -import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:flutter/material.dart'; -import 'package:step_progress_indicator/step_progress_indicator.dart'; -import 'package:styled_text/styled_text.dart'; - -class OTTVerificationPage extends StatefulWidget { - final String email; - final bool isChangeEmail; - final bool isCreateAccountScreen; - final bool isResetPasswordScreen; - - const OTTVerificationPage( - this.email, { - this.isChangeEmail = false, - this.isCreateAccountScreen = false, - this.isResetPasswordScreen = false, - super.key, - }); - - @override - State createState() => _OTTVerificationPageState(); -} - -class _OTTVerificationPageState extends State { - final _verificationCodeController = TextEditingController(); - - Future onPressed() async { - if (widget.isChangeEmail) { - await UserService.instance.changeEmail( - context, - widget.email, - _verificationCodeController.text, - ); - } else { - await UserService.instance.verifyEmail( - context, - _verificationCodeController.text, - isResettingPasswordScreen: widget.isResetPasswordScreen, - ); - } - FocusScope.of(context).unfocus(); - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - title: widget.isCreateAccountScreen - ? Material( - type: MaterialType.transparency, - child: StepProgressIndicator( - totalSteps: 4, - currentStep: 2, - selectedColor: Theme.of(context).colorScheme.alternativeColor, - roundedEdges: const Radius.circular(10), - unselectedColor: - Theme.of(context).colorScheme.stepProgressUnselectedColor, - ), - ) - : null, - ), - body: _getBody(), - floatingActionButton: DynamicFAB( - isKeypadOpen: isKeypadOpen, - isFormValid: _verificationCodeController.text.isNotEmpty, - buttonText: l10n.verify, - onPressedFunction: onPressed, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Widget _getBody() { - final l10n = context.l10n; - return ListView( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(20, 30, 20, 15), - child: Text( - l10n.verifyEmail, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10), - child: Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(0, 0, 0, 12), - child: StyledText( - text: l10n.weHaveSendEmailTo(widget.email), - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 14), - tags: { - 'green': StyledTextTag( - style: TextStyle( - color: Theme.of(context) - .colorScheme - .alternativeColor, - ), - ), - }, - ), - ), - widget.isResetPasswordScreen - ? Text( - l10n.toResetVerifyEmail, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 14), - ) - : Text( - l10n.checkInboxAndSpamFolder, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 14), - ), - ], - ), - ), - SizedBox( - width: MediaQuery.of(context).size.width * 0.2, - height: 1, - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 16, 20, 16), - child: TextFormField( - style: Theme.of(context).textTheme.titleMedium, - onFieldSubmitted: _verificationCodeController.text.isNotEmpty - ? (_) => onPressed() - : null, - decoration: InputDecoration( - filled: true, - hintText: l10n.tapToEnterCode, - contentPadding: const EdgeInsets.all(15), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - ), - controller: _verificationCodeController, - autofocus: true, - autocorrect: false, - keyboardType: TextInputType.number, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Divider( - thickness: 1, - ), - Padding( - padding: const EdgeInsets.all(20), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - UserService.instance.sendOtt( - context, - widget.email, - isCreateAccountScreen: widget.isCreateAccountScreen, - isChangeEmail: widget.isChangeEmail, - isResetPasswordScreen: widget.isResetPasswordScreen, - ); - }, - child: Text( - l10n.resendEmail, - style: Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ], - ), - ), - ], - ), - ], - ); - // ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/password_entry_page.dart b/mobile/apps/auth/lib/ui/account/password_entry_page.dart deleted file mode 100644 index acaf051197..0000000000 --- a/mobile/apps/auth/lib/ui/account/password_entry_page.dart +++ /dev/null @@ -1,515 +0,0 @@ -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/account/recovery_key_page.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:ente_auth/ui/components/models/button_type.dart'; -import 'package:ente_auth/ui/home_page.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/navigation_util.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:logging/logging.dart'; -import 'package:password_strength/password_strength.dart'; -import 'package:styled_text/styled_text.dart'; - -enum PasswordEntryMode { - set, - update, - reset, -} - -class PasswordEntryPage extends StatefulWidget { - final PasswordEntryMode mode; - - const PasswordEntryPage({required this.mode, super.key}); - - @override - State createState() => _PasswordEntryPageState(); -} - -class _PasswordEntryPageState extends State { - static const kMildPasswordStrengthThreshold = 0.4; - static const kStrongPasswordStrengthThreshold = 0.7; - - final _logger = Logger((_PasswordEntryPageState).toString()); - final _passwordController1 = TextEditingController(), - _passwordController2 = TextEditingController(); - final Color _validFieldValueColor = const Color.fromRGBO(45, 194, 98, 0.2); - String? _volatilePassword; - String _passwordInInputBox = ''; - String _passwordInInputConfirmationBox = ''; - double _passwordStrength = 0.0; - bool _password1Visible = false; - bool _password2Visible = false; - final _password1FocusNode = FocusNode(); - final _password2FocusNode = FocusNode(); - bool _password1InFocus = false; - bool _password2InFocus = false; - - bool _passwordsMatch = false; - bool _isPasswordValid = false; - - @override - void initState() { - super.initState(); - _volatilePassword = Configuration.instance.getVolatilePassword(); - if (_volatilePassword != null) { - Future.delayed( - Duration.zero, - () => _showRecoveryCodeDialog( - _volatilePassword!, - usingVolatilePassword: true, - ), - ); - } - _password1FocusNode.addListener(() { - setState(() { - _password1InFocus = _password1FocusNode.hasFocus; - }); - }); - _password2FocusNode.addListener(() { - setState(() { - _password2InFocus = _password2FocusNode.hasFocus; - }); - }); - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - String title = context.l10n.setPasswordTitle; - if (widget.mode == PasswordEntryMode.update) { - title = context.l10n.changePasswordTitle; - } else if (widget.mode == PasswordEntryMode.reset) { - title = context.l10n.resetPasswordTitle; - } else if (_volatilePassword != null) { - title = context.l10n.encryptionKeys; - } - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - leading: widget.mode == PasswordEntryMode.reset - ? Container() - : IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - elevation: 0, - ), - body: _getBody(title), - floatingActionButton: DynamicFAB( - isKeypadOpen: isKeypadOpen, - isFormValid: _passwordsMatch && _isPasswordValid, - buttonText: title, - onPressedFunction: () { - if (widget.mode == PasswordEntryMode.set) { - _showRecoveryCodeDialog(_passwordController1.text); - } else { - _updatePassword(); - } - FocusScope.of(context).unfocus(); - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Widget _getBody(String buttonTextAndHeading) { - final email = Configuration.instance.getEmail(); - var passwordStrengthText = context.l10n.weakStrength; - var passwordStrengthColor = Colors.redAccent; - if (_passwordStrength > kStrongPasswordStrengthThreshold) { - passwordStrengthText = context.l10n.strongStrength; - passwordStrengthColor = Colors.greenAccent; - } else if (_passwordStrength > kMildPasswordStrengthThreshold) { - passwordStrengthText = context.l10n.moderateStrength; - passwordStrengthColor = Colors.orangeAccent; - } - if (_volatilePassword != null) { - return Container(); - } - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: FocusTraversalGroup( - policy: OrderedTraversalPolicy(), - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: 30, - horizontal: 20, - ), - child: Text( - buttonTextAndHeading, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Text( - widget.mode == PasswordEntryMode.set - ? context.l10n.enterPasswordToEncrypt - : context.l10n.enterNewPasswordToEncrypt, - textAlign: TextAlign.start, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 14), - ), - ), - const Padding(padding: EdgeInsets.all(8)), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: StyledText( - text: context.l10n.passwordWarning, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontSize: 14), - tags: { - 'underline': StyledTextTag( - style: - Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - }, - ), - ), - const Padding(padding: EdgeInsets.all(12)), - Visibility( - // hidden textForm for suggesting auto-fill service for saving - // password - visible: false, - child: TextFormField( - autofillHints: const [ - AutofillHints.email, - ], - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: email, - textInputAction: TextInputAction.next, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - autofillHints: const [AutofillHints.newPassword], - onFieldSubmitted: (_) { - do { - FocusScope.of(context).nextFocus(); - } while (FocusScope.of(context).focusedChild!.context == - null); - }, - decoration: InputDecoration( - fillColor: - _isPasswordValid ? _validFieldValueColor : null, - filled: true, - hintText: context.l10n.password, - contentPadding: const EdgeInsets.all(20), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _password1InFocus - ? IconButton( - icon: Icon( - _password1Visible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _password1Visible = !_password1Visible; - }); - }, - ) - : _isPasswordValid - ? Icon( - Icons.check, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - ), - obscureText: !_password1Visible, - controller: _passwordController1, - autofocus: false, - autocorrect: false, - keyboardType: TextInputType.visiblePassword, - onChanged: (password) { - setState(() { - _passwordInInputBox = password; - _passwordStrength = - estimatePasswordStrength(password); - _isPasswordValid = _passwordStrength >= - kMildPasswordStrengthThreshold; - _passwordsMatch = _passwordInInputBox == - _passwordInInputConfirmationBox; - }); - }, - textInputAction: TextInputAction.next, - focusNode: _password1FocusNode, - ), - ), - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), - child: TextFormField( - keyboardType: TextInputType.visiblePassword, - controller: _passwordController2, - obscureText: !_password2Visible, - autofillHints: const [AutofillHints.newPassword], - onEditingComplete: () => - TextInput.finishAutofillContext(), - decoration: InputDecoration( - fillColor: - _passwordsMatch ? _validFieldValueColor : null, - filled: true, - hintText: context.l10n.confirmPassword, - contentPadding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 20, - ), - suffixIcon: _password2InFocus - ? IconButton( - icon: Icon( - _password2Visible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _password2Visible = !_password2Visible; - }); - }, - ) - : _passwordsMatch - ? Icon( - Icons.check, - color: Theme.of(context) - .inputDecorationTheme - .focusedBorder! - .borderSide - .color, - ) - : null, - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - ), - focusNode: _password2FocusNode, - onChanged: (cnfPassword) { - setState(() { - _passwordInInputConfirmationBox = cnfPassword; - if (_passwordInInputBox != '') { - _passwordsMatch = _passwordInInputBox == - _passwordInInputConfirmationBox; - } - }); - }, - ), - ), - Opacity( - opacity: (_passwordInInputBox != '') && _password1InFocus - ? 1 - : 0, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 20, - vertical: 8, - ), - child: Text( - context.l10n.passwordStrength(passwordStrengthText), - style: TextStyle( - color: passwordStrengthColor, - ), - ), - ), - ), - const SizedBox(height: 8), - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - PlatformUtil.openWebView( - context, - context.l10n.howItWorks, - "https://ente.io/architecture", - ); - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: RichText( - text: TextSpan( - text: context.l10n.howItWorks, - style: - Theme.of(context).textTheme.titleMedium!.copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - ), - const Padding(padding: EdgeInsets.all(20)), - ], - ), - ), - ), - ), - ], - ); - } - - void _updatePassword() async { - final logOutFromOthers = await logOutFromOtherDevices(context); - final dialog = - createProgressDialog(context, context.l10n.generatingEncryptionKeys); - await dialog.show(); - try { - final result = await Configuration.instance - .getAttributesForNewPassword(_passwordController1.text); - await UserService.instance.updateKeyAttributes( - result.item1, - result.item2, - logoutOtherDevices: logOutFromOthers, - ); - await dialog.hide(); - showShortToast(context, context.l10n.passwordChangedSuccessfully); - Navigator.of(context).pop(); - if (widget.mode == PasswordEntryMode.reset) { - Navigator.of(context).popUntil((route) => route.isFirst); - } - } catch (e, s) { - _logger.severe(e, s); - await dialog.hide(); - // ignore: unawaited_futures - showGenericErrorDialog( - context: context, - error: e, - ); - } - } - - Future logOutFromOtherDevices(BuildContext context) async { - bool logOutFromOther = true; - await showChoiceDialog( - context, - title: context.l10n.signOutFromOtherDevices, - body: context.l10n.signOutOtherBody, - isDismissible: false, - firstButtonLabel: context.l10n.signOutOtherDevices, - firstButtonType: ButtonType.critical, - firstButtonOnTap: () async { - logOutFromOther = true; - }, - secondButtonLabel: context.l10n.doNotSignOut, - secondButtonOnTap: () async { - logOutFromOther = false; - }, - ); - return logOutFromOther; - } - - Future _showRecoveryCodeDialog( - String password, { - bool usingVolatilePassword = false, - }) async { - final l10n = context.l10n; - final dialog = - createProgressDialog(context, l10n.generatingEncryptionKeysTitle); - await dialog.show(); - try { - if (usingVolatilePassword) { - _logger.info('Using volatile password'); - } - final result = - await Configuration.instance.generateKey(password); - Configuration.instance.resetVolatilePassword(); - await dialog.hide(); - onDone() async { - final dialog = createProgressDialog(context, l10n.pleaseWait); - await dialog.show(); - try { - await UserService.instance.setAttributes(result); - await dialog.hide(); - Configuration.instance.resetVolatilePassword(); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return const HomePage(); - }, - ), - (route) => route.isFirst, - ); - } catch (e, s) { - _logger.severe(e, s); - await dialog.hide(); - // ignore: unawaited_futures - showGenericErrorDialog( - context: context, - error: e, - ); - } - } - - // ignore: unawaited_futures - routeToPage( - context, - RecoveryKeyPage( - result.privateKeyAttributes.recoveryKey, - context.l10n.continueLabel, - showAppBar: false, - isDismissible: false, - onDone: onDone, - showProgressBar: true, - ), - ); - } catch (e) { - _logger.severe(e); - await dialog.hide(); - if (e is UnsupportedError) { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.insecureDevice, - context.l10n.sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease, - ); - } else { - // ignore: unawaited_futures - showGenericErrorDialog( - context: context, - error: e, - ); - } - } - } -} diff --git a/mobile/apps/auth/lib/ui/account/recovery_page.dart b/mobile/apps/auth/lib/ui/account/recovery_page.dart index 137b8ce437..cb3619344d 100644 --- a/mobile/apps/auth/lib/ui/account/recovery_page.dart +++ b/mobile/apps/auth/lib/ui/account/recovery_page.dart @@ -1,7 +1,8 @@ +import 'package:ente_accounts/pages/password_entry_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/ui/account/password_entry_page.dart'; import 'package:ente_auth/ui/common/dynamic_fab.dart'; +import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; @@ -27,10 +28,12 @@ class _RecoveryPageState extends State { await Navigator.of(context).pushReplacement( MaterialPageRoute( builder: (BuildContext context) { - return const PopScope( + return PopScope( canPop: false, child: PasswordEntryPage( - mode: PasswordEntryMode.reset, + Configuration.instance, + PasswordEntryMode.reset, + const HomePage(), ), ); }, diff --git a/mobile/apps/auth/lib/ui/passkey_page.dart b/mobile/apps/auth/lib/ui/passkey_page.dart deleted file mode 100644 index d4b37c615b..0000000000 --- a/mobile/apps/auth/lib/ui/passkey_page.dart +++ /dev/null @@ -1,235 +0,0 @@ -import 'dart:convert'; - -import 'package:app_links/app_links.dart'; -import 'package:ente_accounts/models/two_factor.dart'; -import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/components/buttons/button_widget.dart'; -import 'package:ente_auth/ui/components/models/button_type.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/navigation_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; -import 'package:url_launcher/url_launcher_string.dart'; - -class PasskeyPage extends StatefulWidget { - final String sessionID; - final String totp2FASessionID; - final String accountsUrl; - - const PasskeyPage( - this.sessionID, { - required this.totp2FASessionID, - required this.accountsUrl, - super.key, - }); - - @override - State createState() => _PasskeyPageState(); -} - -class _PasskeyPageState extends State { - final Logger _logger = Logger("PasskeyPage"); - - @override - void initState() { - launchPasskey(); - _initDeepLinks(); - super.initState(); - } - - @override - void dispose() { - super.dispose(); - } - - Future launchPasskey() async { - await launchUrlString( - "${widget.accountsUrl}/passkeys/verify?" - "passkeySessionID=${widget.sessionID}" - "&redirect=enteauth://passkey" - "&clientPackage=io.ente.auth", - mode: LaunchMode.externalApplication, - ); - } - - Future checkStatus() async { - late dynamic response; - try { - response = await UserService.instance - .getTokenForPasskeySession(widget.sessionID); - } on PassKeySessionNotVerifiedError { - showToast(context, context.l10n.passKeyPendingVerification); - return; - } on PassKeySessionExpiredError { - await showErrorDialog( - context, - context.l10n.loginSessionExpired, - context.l10n.loginSessionExpiredDetails, - ); - Navigator.of(context).pop(); - return; - } catch (e, s) { - _logger.severe("failed to check status", e, s); - showGenericErrorDialog(context: context, error: e).ignore(); - return; - } - await UserService.instance.onPassKeyVerified(context, response); - } - - Future _handleDeeplink(String? link) async { - if (!context.mounted || - Configuration.instance.hasConfiguredAccount() || - link == null) { - _logger.warning( - 'ignored deeplink: contextMounted ${context.mounted} hasConfiguredAccount ${Configuration.instance.hasConfiguredAccount()}', - ); - return; - } - try { - if (mounted && link.toLowerCase().startsWith("enteauth://passkey")) { - if (Configuration.instance.isLoggedIn()) { - _logger.info('ignored deeplink: already configured'); - showToast(context, 'Account is already configured.'); - return; - } - final parsedUri = Uri.parse(link); - final sessionID = parsedUri.queryParameters['passkeySessionID']; - if (sessionID != widget.sessionID) { - showToast(context, "Session ID mismatch"); - _logger.warning('ignored deeplink: sessionID mismatch'); - return; - } - final String? authResponse = parsedUri.queryParameters['response']; - String base64String = authResponse!.toString(); - while (base64String.length % 4 != 0) { - base64String += '='; - } - final res = utf8.decode(base64.decode(base64String)); - final json = jsonDecode(res) as Map; - await UserService.instance.onPassKeyVerified(context, json); - } else { - _logger.info('ignored deeplink: $link mounted $mounted'); - } - } catch (e, s) { - _logger.severe('passKey: failed to handle deeplink', e, s); - showGenericErrorDialog(context: context, error: e).ignore(); - } - } - - Future _initDeepLinks() async { - final appLinks = AppLinks(); - // Attach a listener to the stream - appLinks.stringLinkStream.listen( - _handleDeeplink, - onError: (err) { - _logger.severe(err); - }, - ); - return false; - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - return Scaffold( - appBar: AppBar( - title: Text( - l10n.passkeyAuthTitle, - ), - ), - body: _getBody(), - ); - } - - Widget _getBody() { - return Center( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - context.l10n.waitingForVerification, - style: const TextStyle( - height: 1.4, - fontSize: 16, - ), - ), - const SizedBox(height: 16), - ButtonWidget( - buttonType: ButtonType.primary, - labelText: context.l10n.tryAgain, - onTap: () => launchPasskey(), - ), - const SizedBox(height: 16), - ButtonWidget( - buttonType: ButtonType.secondary, - labelText: context.l10n.checkStatus, - onTap: () async { - try { - await checkStatus(); - } catch (e) { - debugPrint('failed to check status %e'); - showGenericErrorDialog(context: context, error: e).ignore(); - } - }, - shouldSurfaceExecutionStates: true, - ), - const Padding(padding: EdgeInsets.all(30)), - if (widget.totp2FASessionID.isNotEmpty) - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - routeToPage( - context, - TwoFactorAuthenticationPage( - widget.totp2FASessionID, - ), - ); - }, - child: Container( - padding: const EdgeInsets.all(10), - child: Center( - child: Text( - context.l10n.loginWithTOTP, - style: const TextStyle( - decoration: TextDecoration.underline, - fontSize: 12, - ), - ), - ), - ), - ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - UserService.instance.recoverTwoFactor( - context, - widget.sessionID, - TwoFactorType.passkey, - ); - }, - child: Container( - padding: const EdgeInsets.all(10), - child: Center( - child: Text( - context.l10n.recoverAccount, - style: const TextStyle( - decoration: TextDecoration.underline, - fontSize: 12, - ), - ), - ), - ), - ), - ], - ), - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart index 568fd788dc..e72585e6de 100644 --- a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart @@ -1,14 +1,15 @@ import 'package:ente_accounts/pages/change_email_dialog.dart'; import 'package:ente_accounts/pages/delete_account_page.dart'; +import 'package:ente_accounts/pages/password_entry_page.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/account/password_entry_page.dart'; import 'package:ente_auth/ui/account/recovery_key_page.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; import 'package:ente_auth/ui/components/menu_item_widget.dart'; +import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/ui/settings/common_settings.dart'; import 'package:ente_auth/utils/dialog_util.dart'; import 'package:ente_auth/utils/navigation_util.dart'; @@ -81,8 +82,10 @@ class AccountSectionWidget extends StatelessWidget { Navigator.of(context).push( MaterialPageRoute( builder: (BuildContext context) { - return const PasswordEntryPage( - mode: PasswordEntryMode.update, + return PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.update, + const HomePage(), ); }, ), From f914263b2f34ec5d63c53b6361617455ee87abd4 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:58:22 +0530 Subject: [PATCH 113/164] Remove old password reentry, recovery key, recovery, and password verification pages; refactor imports and update references in settings and home page. --- mobile/apps/auth/lib/app/view/app.dart | 2 +- mobile/apps/auth/lib/main.dart | 5 +- .../lib/onboarding/view/onboarding_page.dart | 12 +- .../auth/lib/services/passkey_service.dart | 57 - .../apps/auth/lib/services/user_service.dart | 1082 ----------------- .../lib/ui/account/password_reentry_page.dart | 326 ----- .../lib/ui/account/recovery_key_page.dart | 355 ------ .../auth/lib/ui/account/recovery_page.dart | 164 --- .../request_pwd_verification_page.dart | 219 ---- mobile/apps/auth/lib/ui/home_page.dart | 2 +- .../ui/settings/account_section_widget.dart | 5 +- .../settings/notification_banner_widget.dart | 4 +- .../ui/settings/security_section_widget.dart | 7 +- mobile/apps/auth/lib/ui/settings_page.dart | 2 +- 14 files changed, 24 insertions(+), 2218 deletions(-) delete mode 100644 mobile/apps/auth/lib/services/passkey_service.dart delete mode 100644 mobile/apps/auth/lib/services/user_service.dart delete mode 100644 mobile/apps/auth/lib/ui/account/password_reentry_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/recovery_key_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/recovery_page.dart delete mode 100644 mobile/apps/auth/lib/ui/account/request_pwd_verification_page.dart diff --git a/mobile/apps/auth/lib/app/view/app.dart b/mobile/apps/auth/lib/app/view/app.dart index aee7b610b5..833e502975 100644 --- a/mobile/apps/auth/lib/app/view/app.dart +++ b/mobile/apps/auth/lib/app/view/app.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:adaptive_theme/adaptive_theme.dart'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; import "package:ente_auth/l10n/l10n.dart"; @@ -9,7 +10,6 @@ import 'package:ente_auth/locale.dart'; import "package:ente_auth/onboarding/view/onboarding_page.dart"; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/update_service.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/services/window_listener_service.dart'; import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/ui/settings/app_update_dialog.dart'; diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 94cdb778ca..52ad328bbe 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:adaptive_theme/adaptive_theme.dart'; +import 'package:ente_accounts/services/user_service.dart'; import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; @@ -13,11 +14,11 @@ import 'package:ente_auth/services/billing_service.dart'; import 'package:ente_auth/services/notification_service.dart'; import 'package:ente_auth/services/preference_service.dart'; import 'package:ente_auth/services/update_service.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/services/window_listener_service.dart'; import 'package:ente_auth/store/authenticator_db.dart'; import 'package:ente_auth/store/code_display_store.dart'; import 'package:ente_auth/store/code_store.dart'; +import 'package:ente_auth/ui/home_page.dart'; import 'package:ente_auth/ui/utils/icon_utils.dart'; import 'package:ente_auth/utils/directory_utils.dart'; import 'package:ente_auth/utils/platform_util.dart'; @@ -162,7 +163,7 @@ Future _init(bool bool, {String? via}) async { await CodeDisplayStore.instance.init(); await Configuration.instance.init([AuthenticatorDB.instance]); await Network.instance.init(Configuration.instance); - await UserService.instance.init(); + await UserService.instance.init(Configuration.instance, const HomePage()); await AuthenticatorService.instance.init(); await BillingService.instance.init(); await NotificationService.instance.init(); diff --git a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart index c9fcd645f5..5dc8d92763 100644 --- a/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart +++ b/mobile/apps/auth/lib/onboarding/view/onboarding_page.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:ente_accounts/pages/email_entry_page.dart'; import 'package:ente_accounts/pages/login_page.dart'; import 'package:ente_accounts/pages/password_entry_page.dart'; +import 'package:ente_accounts/pages/password_reentry_page.dart'; import 'package:ente_auth/app/view/app.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; @@ -12,7 +13,6 @@ import "package:ente_auth/l10n/l10n.dart"; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/theme/text_style.dart'; import 'package:ente_auth/ui/account/logout_dialog.dart'; -import 'package:ente_auth/ui/account/password_reentry_page.dart'; import 'package:ente_auth/ui/common/gradient_button.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/models/button_result.dart'; @@ -272,7 +272,10 @@ class _OnboardingPageState extends State { ); } else if (Configuration.instance.getKey() == null) { // Yet to decrypt the key - page = const PasswordReentryPage(); + page = PasswordReentryPage( + Configuration.instance, + const HomePage(), + ); } else { // All is well, user just has not subscribed page = const HomePage(); @@ -302,7 +305,10 @@ class _OnboardingPageState extends State { ); } else if (Configuration.instance.getKey() == null) { // Yet to decrypt the key - page = const PasswordReentryPage(); + page = PasswordReentryPage( + Configuration.instance, + const HomePage(), + ); } else { // All is well, user just has not subscribed // page = getSubscriptionPage(isOnBoarding: true); diff --git a/mobile/apps/auth/lib/services/passkey_service.dart b/mobile/apps/auth/lib/services/passkey_service.dart deleted file mode 100644 index 20fd037f3f..0000000000 --- a/mobile/apps/auth/lib/services/passkey_service.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_network/network.dart'; -import 'package:flutter/widgets.dart'; -import 'package:logging/logging.dart'; -import 'package:url_launcher/url_launcher_string.dart'; - -class PasskeyService { - PasskeyService._privateConstructor(); - static final PasskeyService instance = PasskeyService._privateConstructor(); - - final _enteDio = Network.instance.enteDio; - - Future getAccountsUrl() async { - final response = await _enteDio.get( - "/users/accounts-token", - ); - final accountsUrl = response.data!["accountsUrl"] ?? kAccountsUrl; - final jwtToken = response.data!["accountsToken"] as String; - return "$accountsUrl/passkeys?token=$jwtToken"; - } - - Future isPasskeyRecoveryEnabled() async { - final response = await _enteDio.get( - "/users/two-factor/recovery-status", - ); - return response.data!["isPasskeyRecoveryEnabled"] as bool; - } - - Future configurePasskeyRecovery( - String secret, - String userEncryptedSecret, - String userSecretNonce, - ) async { - await _enteDio.post( - "/users/two-factor/passkeys/configure-recovery", - data: { - "secret": secret, - "userSecretCipher": userEncryptedSecret, - "userSecretNonce": userSecretNonce, - }, - ); - } - - Future openPasskeyPage(BuildContext context) async { - try { - final url = await getAccountsUrl(); - await launchUrlString( - url, - mode: LaunchMode.externalApplication, - ); - } catch (e) { - Logger('PasskeyService').severe("failed to open passkey page", e); - showGenericErrorDialog(context: context, error: e).ignore(); - } - } -} diff --git a/mobile/apps/auth/lib/services/user_service.dart b/mobile/apps/auth/lib/services/user_service.dart deleted file mode 100644 index cb676ad237..0000000000 --- a/mobile/apps/auth/lib/services/user_service.dart +++ /dev/null @@ -1,1082 +0,0 @@ -import 'dart:async'; -import "dart:convert"; -import "dart:math"; - -import 'package:bip39/bip39.dart' as bip39; -import 'package:dio/dio.dart'; -import 'package:ente_accounts/models/delete_account.dart'; -import 'package:ente_accounts/models/sessions.dart'; -import 'package:ente_accounts/models/set_keys_request.dart'; -import 'package:ente_accounts/models/set_recovery_key_request.dart'; -import 'package:ente_accounts/models/two_factor.dart'; -import 'package:ente_accounts/models/user_details.dart'; -import 'package:ente_accounts/pages/login_page.dart'; -import 'package:ente_accounts/pages/ott_verification_page.dart'; -import 'package:ente_accounts/pages/passkey_page.dart'; -import 'package:ente_accounts/pages/password_entry_page.dart'; -import 'package:ente_accounts/pages/two_factor_authentication_page.dart'; -import 'package:ente_accounts/pages/two_factor_recovery_page.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/models/api/user/srp.dart'; -import 'package:ente_auth/ui/account/password_reentry_page.dart'; -import 'package:ente_auth/ui/account/recovery_page.dart'; -import 'package:ente_auth/ui/common/progress_dialog.dart'; -import 'package:ente_auth/ui/home_page.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:ente_base/models/key_attributes.dart'; -import 'package:ente_base/models/key_gen_result.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:ente_events/event_bus.dart'; -import 'package:ente_events/models/user_details_changed_event.dart'; -import 'package:ente_network/network.dart'; -import "package:flutter/foundation.dart"; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; -import "package:pointycastle/export.dart"; -import "package:pointycastle/srp/srp6_client.dart"; -import "package:pointycastle/srp/srp6_standard_groups.dart"; -import "package:pointycastle/srp/srp6_util.dart"; -import "package:pointycastle/srp/srp6_verifier_generator.dart"; -import 'package:shared_preferences/shared_preferences.dart'; -import "package:uuid/uuid.dart"; - -class UserService { - static const keyHasEnabledTwoFactor = "has_enabled_two_factor"; - static const keyUserDetails = "user_details"; - static const kReferralSource = "referral_source"; - static const kCanDisableEmailMFA = "can_disable_email_mfa"; - static const kIsEmailMFAEnabled = "is_email_mfa_enabled"; - final SRP6GroupParameters kDefaultSrpGroup = SRP6StandardGroups.rfc5054_4096; - final _dio = Network.instance.getDio(); - final _enteDio = Network.instance.enteDio; - final _logger = Logger((UserService).toString()); - final _config = Configuration.instance; - late SharedPreferences _preferences; - - late ValueNotifier emailValueNotifier; - - UserService._privateConstructor(); - - static final UserService instance = UserService._privateConstructor(); - - Future init() async { - emailValueNotifier = - ValueNotifier(Configuration.instance.getEmail()); - _preferences = await SharedPreferences.getInstance(); - } - - Future sendOtt( - BuildContext context, - String email, { - bool isChangeEmail = false, - bool isCreateAccountScreen = false, - bool isResetPasswordScreen = false, - String? purpose, - }) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final response = await _dio.post( - "${_config.getHttpEndpoint()}/users/ott", - data: { - "email": email, - "purpose": isChangeEmail ? "change" : purpose ?? "", - }, - ); - await dialog.hide(); - if (response.statusCode == 200) { - unawaited( - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return OTTVerificationPage( - email, - isChangeEmail: isChangeEmail, - isCreateAccountScreen: isCreateAccountScreen, - isResetPasswordScreen: isResetPasswordScreen, - ); - }, - ), - ), - ); - return; - } - unawaited(showGenericErrorDialog(context: context, error: null)); - } on DioException catch (e) { - await dialog.hide(); - _logger.info(e); - final String? enteErrCode = e.response?.data["code"]; - if (enteErrCode != null && enteErrCode == "USER_ALREADY_REGISTERED") { - unawaited( - showErrorDialog( - context, - context.l10n.oops, - context.l10n.emailAlreadyRegistered, - ), - ); - } else if (enteErrCode != null && enteErrCode == "USER_NOT_REGISTERED") { - unawaited( - showErrorDialog( - context, - context.l10n.oops, - context.l10n.emailNotRegistered, - ), - ); - } else if (e.response != null && e.response!.statusCode == 403) { - unawaited( - showErrorDialog( - context, - context.l10n.oops, - context.l10n.thisEmailIsAlreadyInUse, - ), - ); - } else { - unawaited(showGenericErrorDialog(context: context, error: e)); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - unawaited(showGenericErrorDialog(context: context, error: e)); - } - } - - Future sendFeedback( - BuildContext context, - String feedback, { - String type = "SubCancellation", - }) async { - await _dio.post( - "${_config.getHttpEndpoint()}/anonymous/feedback", - data: {"feedback": feedback, "type": "type"}, - ); - } - - Future getUserDetailsV2({ - bool memoryCount = false, - bool shouldCache = true, - }) async { - try { - final response = await _enteDio.get( - "/users/details/v2", - queryParameters: { - "memoryCount": memoryCount, - }, - ); - final userDetails = UserDetails.fromMap(response.data); - if (shouldCache) { - if (userDetails.profileData != null) { - await _preferences.setBool( - kIsEmailMFAEnabled, - userDetails.profileData!.isEmailMFAEnabled, - ); - await _preferences.setBool( - kCanDisableEmailMFA, - userDetails.profileData!.canDisableEmailMFA, - ); - } - // handle email change from different client - if (userDetails.email != _config.getEmail()) { - await setEmail(userDetails.email); - } - } - return userDetails; - } catch (e) { - _logger.warning("Failed to fetch", e); - if (e is DioException && e.response?.statusCode == 401) { - throw UnauthorizedError(); - } else { - rethrow; - } - } - } - - Future getActiveSessions() async { - try { - final response = await _enteDio.get("/users/sessions"); - return Sessions.fromMap(response.data); - } on DioException catch (e) { - _logger.info(e); - rethrow; - } - } - - Future terminateSession(String token) async { - try { - await _enteDio.delete( - "/users/session", - queryParameters: { - "token": token, - }, - ); - } on DioException catch (e) { - _logger.info(e); - rethrow; - } - } - - Future leaveFamilyPlan() async { - try { - await _enteDio.delete("/family/leave"); - } on DioException catch (e) { - _logger.warning('failed to leave family plan', e); - rethrow; - } - } - - Future logout(BuildContext context) async { - try { - final response = await _enteDio.post("/users/logout"); - if (response.statusCode == 200) { - await Configuration.instance.logout(); - Navigator.of(context).popUntil((route) => route.isFirst); - } else { - throw Exception("Log out action failed"); - } - } catch (e) { - _logger.severe(e); - // check if token is already invalid - if (e is DioException && e.response?.statusCode == 401) { - await Configuration.instance.logout(); - Navigator.of(context).popUntil((route) => route.isFirst); - return; - } - //This future is for waiting for the dialog from which logout() is called - //to close and only then to show the error dialog. - Future.delayed( - const Duration(milliseconds: 150), - () => showGenericErrorDialog(context: context, error: e), - ); - rethrow; - } - } - - Future getDeleteChallenge( - BuildContext context, - ) async { - try { - final response = await _enteDio.get("/users/delete-challenge"); - if (response.statusCode == 200) { - return DeleteChallengeResponse( - allowDelete: response.data["allowDelete"] as bool, - encryptedChallenge: response.data["encryptedChallenge"], - ); - } else { - throw Exception("delete action failed"); - } - } catch (e) { - _logger.severe(e); - await showGenericErrorDialog( - context: context, - error: e, - ); - return null; - } - } - - Future deleteAccount( - BuildContext context, - String challengeResponse, - ) async { - try { - final response = await _enteDio.delete( - "/users/delete", - data: { - "challenge": challengeResponse, - }, - ); - if (response.statusCode == 200) { - // clear data - await Configuration.instance.logout(); - } else { - throw Exception("delete action failed"); - } - } catch (e) { - _logger.severe(e); - rethrow; - } - } - - Future getTokenForPasskeySession(String sessionID) async { - try { - final response = await _dio.get( - "${_config.getHttpEndpoint()}/users/two-factor/passkeys/get-token", - queryParameters: { - "sessionID": sessionID, - }, - ); - return response.data; - } on DioException catch (e) { - if (e.response != null) { - if (e.response!.statusCode == 404 || e.response!.statusCode == 410) { - throw PassKeySessionExpiredError(); - } - if (e.response!.statusCode == 400) { - throw PassKeySessionNotVerifiedError(); - } - } - rethrow; - } catch (e, s) { - _logger.severe("unexpected error", e, s); - rethrow; - } - } - - Future onPassKeyVerified(BuildContext context, Map response) async { - final ProgressDialog dialog = - createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final userPassword = _config.getVolatilePassword(); - await _saveConfiguration(response); - if (userPassword == null) { - await dialog.hide(); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return const PasswordReentryPage(); - }, - ), - (route) => route.isFirst, - ); - } else { - Widget page; - if (_config.getEncryptedToken() != null) { - await _config.decryptSecretsAndGetKeyEncKey( - userPassword, - _config.getKeyAttributes()!, - ); - _config.resetVolatilePassword(); - page = const HomePage(); - } else { - throw Exception("unexpected response during passkey verification"); - } - await dialog.hide(); - - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return page; - }, - ), - (route) => route.isFirst, - ); - } - } catch (e) { - _logger.severe(e); - await dialog.hide(); - rethrow; - } - } - - Future verifyEmail( - BuildContext context, - String ott, { - bool isResettingPasswordScreen = false, - }) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - final verifyData = { - "email": _config.getEmail(), - "ott": ott, - }; - if (!_config.isLoggedIn()) { - verifyData["source"] = 'auth:${_getRefSource()}'; - } - try { - final response = await _dio.post( - "${_config.getHttpEndpoint()}/users/verify-email", - data: verifyData, - ); - await dialog.hide(); - if (response.statusCode == 200) { - Widget page; - final String passkeySessionID = response.data["passkeySessionID"]; - final String accountsUrl = response.data["accountsUrl"] ?? kAccountsUrl; - String twoFASessionID = response.data["twoFactorSessionID"]; - if (twoFASessionID.isEmpty && - response.data["twoFactorSessionIDV2"] != null) { - twoFASessionID = response.data["twoFactorSessionIDV2"]; - } - if (passkeySessionID.isNotEmpty) { - page = PasskeyPage( - Configuration.instance, - passkeySessionID, - totp2FASessionID: twoFASessionID, - accountsUrl: accountsUrl, - ); - } else if (twoFASessionID.isNotEmpty) { - page = TwoFactorAuthenticationPage(twoFASessionID); - } else { - await _saveConfiguration(response); - if (Configuration.instance.getEncryptedToken() != null) { - if (isResettingPasswordScreen) { - page = const RecoveryPage(); - } else { - page = const PasswordReentryPage(); - } - } else { - page = PasswordEntryPage( - Configuration.instance, - PasswordEntryMode.set, - const HomePage(), - ); - } - } - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return page; - }, - ), - (route) => route.isFirst, - ); - } else { - // should never reach here - throw Exception("unexpected response during email verification"); - } - } on DioException catch (e) { - _logger.info(e); - await dialog.hide(); - if (e.response != null && e.response!.statusCode == 410) { - await showErrorDialog( - context, - context.l10n.oops, - context.l10n.yourVerificationCodeHasExpired, - ); - Navigator.of(context).pop(); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.incorrectCode, - context.l10n.sorryTheCodeYouveEnteredIsIncorrect, - ); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.verificationFailedPleaseTryAgain, - ); - } - } - - Future setEmail(String email) async { - await _config.setEmail(email); - emailValueNotifier.value = email; - } - - Future changeEmail( - BuildContext context, - String email, - String ott, - ) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final response = await _enteDio.post( - "/users/change-email", - data: { - "email": email, - "ott": ott, - }, - ); - await dialog.hide(); - if (response.statusCode == 200) { - showShortToast(context, context.l10n.emailChangedTo(email)); - await setEmail(email); - Navigator.of(context).popUntil((route) => route.isFirst); - Bus.instance.fire(UserDetailsChangedEvent()); - return; - } - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.verificationFailedPleaseTryAgain, - ); - } on DioException catch (e) { - await dialog.hide(); - if (e.response != null && e.response!.statusCode == 403) { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.thisEmailIsAlreadyInUse, - ); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.incorrectCode, - context.l10n.authenticationFailedPleaseTryAgain, - ); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.verificationFailedPleaseTryAgain, - ); - } - } - - Future setAttributes(KeyGenResult result) async { - try { - await registerOrUpdateSrp(result.loginKey); - await _enteDio.put( - "/users/attributes", - data: { - "keyAttributes": result.keyAttributes.toMap(), - }, - ); - await _config.setKey(result.privateKeyAttributes.key); - await _config.setSecretKey(result.privateKeyAttributes.secretKey); - await _config.setKeyAttributes(result.keyAttributes); - } catch (e) { - _logger.severe(e); - rethrow; - } - } - - Future getSrpAttributes(String email) async { - try { - final response = await _dio.get( - "${_config.getHttpEndpoint()}/users/srp/attributes", - queryParameters: { - "email": email, - }, - ); - if (response.statusCode == 200) { - return SrpAttributes.fromMap(response.data); - } else { - throw Exception("get-srp-attributes action failed"); - } - } on DioException catch (e) { - if (e.response != null && e.response!.statusCode == 404) { - throw SrpSetupNotCompleteError(); - } - rethrow; - } catch (e) { - rethrow; - } - } - - Future registerOrUpdateSrp( - Uint8List loginKey, { - SetKeysRequest? setKeysRequest, - bool logOutOtherDevices = false, - }) async { - try { - final String username = const Uuid().v4().toString(); - final SecureRandom random = _getSecureRandom(); - final Uint8List identity = Uint8List.fromList(utf8.encode(username)); - final Uint8List password = loginKey; - final Uint8List salt = random.nextBytes(16); - final gen = SRP6VerifierGenerator( - group: kDefaultSrpGroup, - digest: Digest('SHA-256'), - ); - final v = gen.generateVerifier(salt, identity, password); - - final client = SRP6Client( - group: kDefaultSrpGroup, - digest: Digest('SHA-256'), - random: random, - ); - - final A = client.generateClientCredentials(salt, identity, password); - final request = SetupSRPRequest( - srpUserID: username, - srpSalt: base64Encode(salt), - srpVerifier: base64Encode(SRP6Util.encodeBigInt(v)), - srpA: base64Encode(SRP6Util.encodeBigInt(A!)), - isUpdate: false, - ); - final response = await _enteDio.post( - "/users/srp/setup", - data: request.toMap(), - ); - if (response.statusCode == 200) { - final SetupSRPResponse setupSRPResponse = - SetupSRPResponse.fromJson(response.data); - final serverB = - SRP6Util.decodeBigInt(base64Decode(setupSRPResponse.srpB)); - // ignore: unused_local_variable - final clientS = client.calculateSecret(serverB); - final clientM = client.calculateClientEvidenceMessage(); - - if (setKeysRequest == null) { - await _enteDio.post( - "/users/srp/complete", - data: { - 'setupID': setupSRPResponse.setupID, - 'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)), - }, - ); - } else { - await _enteDio.post( - "/users/srp/update", - data: { - 'setupID': setupSRPResponse.setupID, - 'srpM1': base64Encode(SRP6Util.encodeBigInt(clientM!)), - 'updatedKeyAttr': setKeysRequest.toMap(), - 'logOutOtherDevices': logOutOtherDevices, - }, - ); - } - } else { - throw Exception("register-srp action failed"); - } - } catch (e, s) { - _logger.severe("failed to register srp", e, s); - rethrow; - } - } - - SecureRandom _getSecureRandom() { - final List seeds = []; - final random = Random.secure(); - for (int i = 0; i < 32; i++) { - seeds.add(random.nextInt(255)); - } - final secureRandom = FortunaRandom(); - secureRandom.seed(KeyParameter(Uint8List.fromList(seeds))); - return secureRandom; - } - - Future verifyEmailViaPassword( - BuildContext context, - SrpAttributes srpAttributes, - String userPassword, - ProgressDialog dialog, - ) async { - late Uint8List keyEncryptionKey; - _logger.finest('Start deriving key'); - keyEncryptionKey = await CryptoUtil.deriveKey( - utf8.encode(userPassword), - CryptoUtil.base642bin(srpAttributes.kekSalt), - srpAttributes.memLimit, - srpAttributes.opsLimit, - ); - _logger.finest('keyDerivation done, derive LoginKey'); - final loginKey = await CryptoUtil.deriveLoginKey(keyEncryptionKey); - final Uint8List identity = Uint8List.fromList( - utf8.encode(srpAttributes.srpUserID), - ); - _logger.finest('longinKey derivation done'); - final Uint8List salt = base64Decode(srpAttributes.srpSalt); - final Uint8List password = loginKey; - final SecureRandom random = _getSecureRandom(); - - final client = SRP6Client( - group: kDefaultSrpGroup, - digest: Digest('SHA-256'), - random: random, - ); - - final A = client.generateClientCredentials(salt, identity, password); - final createSessionResponse = await _dio.post( - "${_config.getHttpEndpoint()}/users/srp/create-session", - data: { - "srpUserID": srpAttributes.srpUserID, - "srpA": base64Encode(SRP6Util.getPadded(A!, 512)), - }, - ); - final String sessionID = createSessionResponse.data["sessionID"]; - final String srpB = createSessionResponse.data["srpB"]; - - final serverB = SRP6Util.decodeBigInt(base64Decode(srpB)); - - // ignore: unused_local_variable - final clientS = client.calculateSecret(serverB); - final clientM = client.calculateClientEvidenceMessage(); - final response = await _dio.post( - "${_config.getHttpEndpoint()}/users/srp/verify-session", - data: { - "sessionID": sessionID, - "srpUserID": srpAttributes.srpUserID, - "srpM1": base64Encode(SRP6Util.getPadded(clientM!, 32)), - }, - ); - if (response.statusCode == 200) { - Widget? page; - final String passkeySessionID = response.data["passkeySessionID"]; - final String accountsUrl = response.data["accountsUrl"] ?? kAccountsUrl; - String twoFASessionID = response.data["twoFactorSessionID"]; - if (twoFASessionID.isEmpty && - response.data["twoFactorSessionIDV2"] != null) { - twoFASessionID = response.data["twoFactorSessionIDV2"]; - } - Configuration.instance.setVolatilePassword(userPassword); - if (passkeySessionID.isNotEmpty) { - page = PasskeyPage( - Configuration.instance, - passkeySessionID, - totp2FASessionID: twoFASessionID, - accountsUrl: accountsUrl, - ); - } else if (twoFASessionID.isNotEmpty) { - page = TwoFactorAuthenticationPage(twoFASessionID); - } else { - await _saveConfiguration(response); - if (Configuration.instance.getEncryptedToken() != null) { - await Configuration.instance.decryptSecretsAndGetKeyEncKey( - userPassword, - Configuration.instance.getKeyAttributes()!, - keyEncryptionKey: keyEncryptionKey, - ); - page = const HomePage(); - } else { - throw Exception("unexpected response during email verification"); - } - } - await dialog.hide(); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return page!; - }, - ), - (route) => route.isFirst, - ); - } else { - // should never reach here - throw Exception("unexpected response during email verification"); - } - } - - Future updateKeyAttributes( - KeyAttributes keyAttributes, - Uint8List loginKey, { - required bool logoutOtherDevices, - }) async { - try { - final setKeyRequest = SetKeysRequest( - kekSalt: keyAttributes.kekSalt, - encryptedKey: keyAttributes.encryptedKey, - keyDecryptionNonce: keyAttributes.keyDecryptionNonce, - memLimit: keyAttributes.memLimit, - opsLimit: keyAttributes.opsLimit, - ); - await registerOrUpdateSrp( - loginKey, - setKeysRequest: setKeyRequest, - logOutOtherDevices: logoutOtherDevices, - ); - await _config.setKeyAttributes(keyAttributes); - } catch (e) { - _logger.severe(e); - rethrow; - } - } - - Future setRecoveryKey(KeyAttributes keyAttributes) async { - try { - final setRecoveryKeyRequest = SetRecoveryKeyRequest( - keyAttributes.masterKeyEncryptedWithRecoveryKey, - keyAttributes.masterKeyDecryptionNonce, - keyAttributes.recoveryKeyEncryptedWithMasterKey, - keyAttributes.recoveryKeyDecryptionNonce, - ); - await _enteDio.put( - "/users/recovery-key", - data: setRecoveryKeyRequest.toMap(), - ); - await _config.setKeyAttributes(keyAttributes); - } catch (e) { - _logger.severe(e); - rethrow; - } - } - - Future verifyTwoFactor( - BuildContext context, - String sessionID, - String code, - ) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final response = await _dio.post( - "${_config.getHttpEndpoint()}/users/two-factor/verify", - data: { - "sessionID": sessionID, - "code": code, - }, - ); - await dialog.hide(); - if (response.statusCode == 200) { - showShortToast(context, context.l10n.authenticationSuccessful); - await _saveConfiguration(response); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return const PasswordReentryPage(); - }, - ), - (route) => route.isFirst, - ); - } - } on DioException catch (e) { - await dialog.hide(); - _logger.severe(e); - if (e.response != null && e.response!.statusCode == 404) { - showToast(context, "Session expired"); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return LoginPage(Configuration.instance); - }, - ), - (route) => route.isFirst, - ); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.incorrectCode, - context.l10n.authenticationFailedPleaseTryAgain, - ); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.authenticationFailedPleaseTryAgain, - ); - } - } - - Future recoverTwoFactor( - BuildContext context, - String sessionID, - TwoFactorType type, - ) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final response = await _dio.get( - "${_config.getHttpEndpoint()}/users/two-factor/recover", - queryParameters: { - "sessionID": sessionID, - "twoFactorType": twoFactorTypeToString(type), - }, - ); - await dialog.hide(); - if (response.statusCode == 200) { - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return TwoFactorRecoveryPage( - type, - sessionID, - response.data["encryptedSecret"], - response.data["secretDecryptionNonce"], - ); - }, - ), - (route) => route.isFirst, - ); - } - } on DioException catch (e) { - await dialog.hide(); - _logger.severe(e); - if (e.response != null && e.response!.statusCode == 404) { - showToast(context, context.l10n.sessionExpired); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return LoginPage(Configuration.instance); - }, - ), - (route) => route.isFirst, - ); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.somethingWentWrongPleaseTryAgain, - ); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.somethingWentWrongPleaseTryAgain, - ); - } finally { - await dialog.hide(); - } - } - - Future removeTwoFactor( - BuildContext context, - TwoFactorType type, - String sessionID, - String recoveryKey, - String encryptedSecret, - String secretDecryptionNonce, - ) async { - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - String secret; - try { - if (recoveryKey.contains(' ')) { - if (recoveryKey.split(' ').length != mnemonicKeyWordCount) { - throw AssertionError( - 'recovery code should have $mnemonicKeyWordCount words', - ); - } - recoveryKey = bip39.mnemonicToEntropy(recoveryKey); - } - secret = CryptoUtil.bin2base64( - await CryptoUtil.decrypt( - CryptoUtil.base642bin(encryptedSecret), - CryptoUtil.hex2bin(recoveryKey.trim()), - CryptoUtil.base642bin(secretDecryptionNonce), - ), - ); - } catch (e) { - await dialog.hide(); - await showErrorDialog( - context, - context.l10n.incorrectRecoveryKey, - context.l10n.theRecoveryKeyYouEnteredIsIncorrect, - ); - return; - } - try { - final response = await _dio.post( - "${_config.getHttpEndpoint()}/users/two-factor/remove", - data: { - "sessionID": sessionID, - "secret": secret, - "twoFactorType": twoFactorTypeToString(type), - }, - ); - await dialog.hide(); - if (response.statusCode == 200) { - showShortToast( - context, - context.l10n.twofactorAuthenticationSuccessfullyReset, - ); - await _saveConfiguration(response); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return const PasswordReentryPage(); - }, - ), - (route) => route.isFirst, - ); - } - } on DioException catch (e) { - await dialog.hide(); - _logger.severe(e); - if (e.response != null && e.response!.statusCode == 404) { - showToast(context, "Session expired"); - // ignore: unawaited_futures - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return LoginPage(Configuration.instance); - }, - ), - (route) => route.isFirst, - ); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.somethingWentWrongPleaseTryAgain, - ); - } - } catch (e) { - await dialog.hide(); - _logger.severe(e); - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.oops, - context.l10n.somethingWentWrongPleaseTryAgain, - ); - } finally { - await dialog.hide(); - } - } - - Future _saveConfiguration(dynamic response) async { - final responseData = response is Map ? response : response.data as Map?; - if (responseData == null) return; - - await Configuration.instance.setUserID(responseData["id"]); - if (responseData["encryptedToken"] != null) { - await Configuration.instance - .setEncryptedToken(responseData["encryptedToken"]); - await Configuration.instance.setKeyAttributes( - KeyAttributes.fromMap(responseData["keyAttributes"]), - ); - } else { - await Configuration.instance.setToken(responseData["token"]); - } - } - - bool? canDisableEmailMFA() { - return _preferences.getBool(kCanDisableEmailMFA); - } - - bool hasEmailMFAEnabled() { - return _preferences.getBool(kIsEmailMFAEnabled) ?? true; - } - - Future updateEmailMFA(bool isEnabled) async { - try { - await _enteDio.put( - "/users/email-mfa", - data: { - "isEnabled": isEnabled, - }, - ); - await _preferences.setBool(kIsEmailMFAEnabled, isEnabled); - } catch (e) { - _logger.severe("Failed to update email mfa", e); - rethrow; - } - } - - Future setRefSource(String refSource) async { - await _preferences.setString(kReferralSource, refSource); - } - - String _getRefSource() { - return _preferences.getString(kReferralSource) ?? ""; - } -} diff --git a/mobile/apps/auth/lib/ui/account/password_reentry_page.dart b/mobile/apps/auth/lib/ui/account/password_reentry_page.dart deleted file mode 100644 index 600d6ada98..0000000000 --- a/mobile/apps/auth/lib/ui/account/password_reentry_page.dart +++ /dev/null @@ -1,326 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/errors.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; -import 'package:ente_auth/ui/account/recovery_page.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:ente_auth/ui/components/buttons/button_widget.dart'; -import 'package:ente_auth/ui/home_page.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/email_util.dart'; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:flutter/material.dart'; -import 'package:logging/logging.dart'; - -class PasswordReentryPage extends StatefulWidget { - const PasswordReentryPage({super.key}); - - @override - State createState() => _PasswordReentryPageState(); -} - -class _PasswordReentryPageState extends State { - final _logger = Logger((_PasswordReentryPageState).toString()); - final _passwordController = TextEditingController(); - final FocusNode _passwordFocusNode = FocusNode(); - String? email; - bool _passwordInFocus = false; - bool _passwordVisible = false; - String? _volatilePassword; - - @override - void initState() { - super.initState(); - email = Configuration.instance.getEmail(); - _volatilePassword = Configuration.instance.getVolatilePassword(); - if (_volatilePassword != null) { - _passwordController.text = _volatilePassword!; - Future.delayed( - Duration.zero, - () => verifyPassword(_volatilePassword!, usingVolatilePassword: true), - ); - } - _passwordFocusNode.addListener(() { - setState(() { - _passwordInFocus = _passwordFocusNode.hasFocus; - }); - }); - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - body: _getBody(), - floatingActionButton: DynamicFAB( - key: const ValueKey("verifyPasswordButton"), - isKeypadOpen: isKeypadOpen, - isFormValid: _passwordController.text.isNotEmpty, - buttonText: context.l10n.verifyPassword, - onPressedFunction: () async { - FocusScope.of(context).unfocus(); - await verifyPassword(_passwordController.text); - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Future verifyPassword( - String password, { - bool usingVolatilePassword = false, - }) async { - FocusScope.of(context).unfocus(); - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - if (usingVolatilePassword) { - _logger.info("Using volatile password"); - } - try { - final kek = await Configuration.instance.decryptSecretsAndGetKeyEncKey( - password, - Configuration.instance.getKeyAttributes()!, - ); - _registerSRPForExistingUsers(kek).ignore(); - } on KeyDerivationError catch (e, s) { - _logger.severe("Password verification failed", e, s); - await dialog.hide(); - final dialogChoice = await showChoiceDialog( - context, - title: context.l10n.recreatePasswordTitle, - body: context.l10n.recreatePasswordBody, - firstButtonLabel: context.l10n.useRecoveryKey, - ); - if (dialogChoice!.action == ButtonAction.first) { - // ignore: unawaited_futures - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return const RecoveryPage(); - }, - ), - ); - } - return; - } catch (e, s) { - _logger.severe("Password verification failed", e, s); - await dialog.hide(); - final dialogChoice = await showChoiceDialog( - context, - title: context.l10n.incorrectPasswordTitle, - body: context.l10n.pleaseTryAgain, - firstButtonLabel: context.l10n.contactSupport, - secondButtonLabel: context.l10n.ok, - ); - if (dialogChoice!.action == ButtonAction.first) { - await sendLogs( - context, - context.l10n.contactSupport, - postShare: () {}, - ); - } - return; - } - Configuration.instance.resetVolatilePassword(); - await dialog.hide(); - unawaited( - Navigator.of(context).pushAndRemoveUntil( - MaterialPageRoute( - builder: (BuildContext context) { - return const HomePage(); - }, - ), - (route) => false, - ), - ); - } - - Future _registerSRPForExistingUsers(Uint8List key) async { - bool shouldSetupSRP = false; - try { - // ignore: unused_local_variable - final attr = await UserService.instance.getSrpAttributes(email!); - } on SrpSetupNotCompleteError { - shouldSetupSRP = true; - } catch (e, s) { - _logger.severe("error while fetching attr", e, s); - } - if (shouldSetupSRP) { - try { - final Uint8List loginKey = await CryptoUtil.deriveLoginKey(key); - await UserService.instance.registerOrUpdateSrp(loginKey); - } catch (e, s) { - _logger.severe("error while setting up srp for existing users", e, s); - } - } - } - - Widget _getBody() { - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: ListView( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 30, horizontal: 20), - child: Text( - context.l10n.welcomeBack, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Visibility( - // hidden textForm for suggesting auto-fill service for saving - // password - visible: false, - child: TextFormField( - autofillHints: const [ - AutofillHints.email, - ], - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: email, - textInputAction: TextInputAction.next, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: TextFormField( - key: const ValueKey("passwordInputField"), - autofillHints: const [AutofillHints.password], - decoration: InputDecoration( - hintText: context.l10n.enterYourPasswordHint, - filled: true, - contentPadding: const EdgeInsets.all(20), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _passwordInFocus - ? IconButton( - icon: Icon( - _passwordVisible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _passwordVisible = !_passwordVisible; - }); - }, - ) - : null, - ), - style: const TextStyle( - fontSize: 14, - ), - controller: _passwordController, - autofocus: true, - autocorrect: false, - obscureText: !_passwordVisible, - keyboardType: TextInputType.visiblePassword, - focusNode: _passwordFocusNode, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 18), - child: Divider( - thickness: 1, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - Navigator.of(context).push( - MaterialPageRoute( - builder: (BuildContext context) { - return const RecoveryPage(); - }, - ), - ); - }, - child: Center( - child: Text( - context.l10n.forgotPassword, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () async { - final dialog = createProgressDialog( - context, - context.l10n.pleaseWait, - ); - await dialog.show(); - await Configuration.instance.logout(); - await dialog.hide(); - Navigator.of(context) - .popUntil((route) => route.isFirst); - }, - child: Center( - child: Text( - context.l10n.changeEmail, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - ], - ), - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/recovery_key_page.dart b/mobile/apps/auth/lib/ui/account/recovery_key_page.dart deleted file mode 100644 index 0422a9e2b8..0000000000 --- a/mobile/apps/auth/lib/ui/account/recovery_key_page.dart +++ /dev/null @@ -1,355 +0,0 @@ -import 'dart:convert'; -import 'dart:io' as io; - -import 'package:bip39/bip39.dart' as bip39; -import 'package:dotted_border/dotted_border.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/core/constants.dart'; -import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/ui/common/gradient_button.dart'; -import 'package:ente_auth/utils/platform_util.dart'; -import 'package:ente_auth/utils/share_utils.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:file_saver/file_saver.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:share_plus/share_plus.dart'; -import 'package:step_progress_indicator/step_progress_indicator.dart'; - -class RecoveryKeyPage extends StatefulWidget { - final bool? showAppBar; - final String recoveryKey; - final String doneText; - final Function()? onDone; - final bool? isDismissible; - final String? title; - final String? text; - final String? subText; - final bool showProgressBar; - - const RecoveryKeyPage( - this.recoveryKey, - this.doneText, { - super.key, - this.showAppBar, - this.onDone, - this.isDismissible, - this.title, - this.text, - this.subText, - this.showProgressBar = false, - }); - - @override - State createState() => _RecoveryKeyPageState(); -} - -class _RecoveryKeyPageState extends State { - bool _hasTriedToSave = false; - final _recoveryKeyFile = io.File( - "${Configuration.instance.getTempDirectory()}ente-recovery-key.txt", - ); - - @override - Widget build(BuildContext context) { - final String recoveryKey = bip39.entropyToMnemonic(widget.recoveryKey); - if (recoveryKey.split(' ').length != mnemonicKeyWordCount) { - throw AssertionError( - 'recovery code should have $mnemonicKeyWordCount words', - ); - } - final double topPadding = widget.showAppBar! - ? 40 - : widget.showProgressBar - ? 32 - : 120; - - Future copy() async { - await Clipboard.setData( - ClipboardData( - text: recoveryKey, - ), - ); - showShortToast( - context, - context.l10n.recoveryKeyCopiedToClipboard, - ); - setState(() { - _hasTriedToSave = true; - }); - } - - return Scaffold( - appBar: widget.showProgressBar - ? AppBar( - automaticallyImplyLeading: false, - elevation: 0, - title: Hero( - tag: "recovery_key", - child: StepProgressIndicator( - totalSteps: 4, - currentStep: 3, - selectedColor: Theme.of(context).colorScheme.alternativeColor, - roundedEdges: const Radius.circular(10), - unselectedColor: - Theme.of(context).colorScheme.stepProgressUnselectedColor, - ), - ), - ) - : widget.showAppBar! - ? AppBar( - elevation: 0, - title: Text(widget.title ?? context.l10n.recoveryKey), - ) - : null, - body: Padding( - padding: EdgeInsets.fromLTRB(20, topPadding, 20, 20), - child: LayoutBuilder( - builder: (context, constraints) { - return SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: constraints.maxWidth, - minHeight: constraints.maxHeight, - ), - child: IntrinsicHeight( - child: Column( - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - widget.showAppBar! - ? const SizedBox.shrink() - : Text( - widget.title ?? context.l10n.recoveryKey, - style: Theme.of(context).textTheme.headlineMedium, - ), - Padding( - padding: EdgeInsets.all(widget.showAppBar! ? 0 : 12), - ), - Text( - widget.text ?? context.l10n.recoveryKeyOnForgotPassword, - style: Theme.of(context).textTheme.titleMedium, - ), - const Padding(padding: EdgeInsets.only(top: 24)), - Container( - padding: const EdgeInsets.all(1), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(8), - gradient: const LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Color(0x8E9610D6), - Color(0x8E9F4FC6), - ], - stops: [0.0, 0.9753], - ), - ), - child: DottedBorder( - padding: EdgeInsets.zero, - borderType: BorderType.RRect, - strokeWidth: 1, - color: const Color(0xFF6B6B6B), - dashPattern: const [6, 6], - radius: const Radius.circular(8), - child: SizedBox( - width: double.infinity, - child: Stack( - children: [ - Column( - children: [ - Builder( - builder: (context) { - final content = Container( - padding: const EdgeInsets.all(20), - width: double.infinity, - child: Text( - recoveryKey, - textAlign: TextAlign.justify, - style: Theme.of(context) - .textTheme - .bodyLarge, - ), - ); - - if (PlatformUtil.isMobile()) { - return GestureDetector( - onTap: () async => await copy(), - child: content, - ); - } else { - return SelectableRegion( - focusNode: FocusNode(), - selectionControls: - PlatformUtil.selectionControls, - child: content, - ); - } - }, - ), - ], - ), - Positioned( - right: 0, - top: 0, - child: PlatformCopy( - onPressed: copy, - ), - ), - ], - ), - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 20), - child: Text( - widget.subText ?? - context.l10n.recoveryKeySaveDescription, - style: Theme.of(context).textTheme.bodyLarge, - ), - ), - Expanded( - child: Container( - alignment: Alignment.bottomCenter, - width: double.infinity, - padding: const EdgeInsets.fromLTRB(10, 10, 10, 42), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: _saveOptions(context, recoveryKey), - ), - ), - ), - ], - ), - ), - ), - ); - }, - ), - ), - ); - } - - List _saveOptions(BuildContext context, String recoveryKey) { - final List childrens = []; - if (!_hasTriedToSave) { - childrens.add( - SizedBox( - height: 56, - child: ElevatedButton( - style: Theme.of(context).colorScheme.optionalActionButtonStyle, - onPressed: () async { - await _saveKeys(); - }, - child: Text(context.l10n.doThisLater), - ), - ), - ); - childrens.add(const SizedBox(height: 10)); - } - - childrens.add( - GradientButton( - onTap: () async { - await shareDialog( - context, - context.l10n.recoveryKey, - saveAction: () async { - await _saveRecoveryKey(recoveryKey); - }, - sendAction: () async { - await _shareRecoveryKey(recoveryKey); - }, - ); - }, - text: context.l10n.saveKey, - ), - ); - - if (_hasTriedToSave) { - childrens.add(const SizedBox(height: 10)); - childrens.add( - SizedBox( - height: 56, - child: ElevatedButton( - child: Text(widget.doneText), - onPressed: () async { - await _saveKeys(); - }, - ), - ), - ); - } - childrens.add(const SizedBox(height: 12)); - return childrens; - } - - Future _saveRecoveryKey(String recoveryKey) async { - final bytes = utf8.encode(recoveryKey); - final time = DateTime.now().millisecondsSinceEpoch; - - await PlatformUtil.shareFile( - "ente_recovery_key_$time", - "txt", - bytes, - MimeType.text, - ); - - if (mounted) { - showToast( - context, - context.l10n.recoveryKeySaved, - ); - setState(() { - _hasTriedToSave = true; - }); - } - } - - Future _shareRecoveryKey(String recoveryKey) async { - if (_recoveryKeyFile.existsSync()) { - await _recoveryKeyFile.delete(); - } - _recoveryKeyFile.writeAsStringSync(recoveryKey); - await Share.shareXFiles([XFile(_recoveryKeyFile.path)]); - Future.delayed(const Duration(milliseconds: 500), () { - if (mounted) { - setState(() { - _hasTriedToSave = true; - }); - } - }); - } - - Future _saveKeys() async { - Navigator.of(context).pop(); - if (_recoveryKeyFile.existsSync()) { - await _recoveryKeyFile.delete(); - } - widget.onDone!(); - } -} - -class PlatformCopy extends StatelessWidget { - const PlatformCopy({ - super.key, - required this.onPressed, - }); - - final void Function() onPressed; - - @override - Widget build(BuildContext context) { - return IconButton( - onPressed: () => onPressed(), - visualDensity: VisualDensity.compact, - icon: const Icon( - Icons.copy, - size: 16, - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/recovery_page.dart b/mobile/apps/auth/lib/ui/account/recovery_page.dart deleted file mode 100644 index cb3619344d..0000000000 --- a/mobile/apps/auth/lib/ui/account/recovery_page.dart +++ /dev/null @@ -1,164 +0,0 @@ -import 'package:ente_accounts/pages/password_entry_page.dart'; -import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import 'package:ente_auth/ui/home_page.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; -import 'package:ente_auth/utils/toast_util.dart'; -import 'package:flutter/material.dart'; - -class RecoveryPage extends StatefulWidget { - const RecoveryPage({super.key}); - - @override - State createState() => _RecoveryPageState(); -} - -class _RecoveryPageState extends State { - final _recoveryKey = TextEditingController(); - - Future onPressed() async { - FocusScope.of(context).unfocus(); - final dialog = createProgressDialog(context, "Decrypting..."); - await dialog.show(); - try { - await Configuration.instance.recover(_recoveryKey.text.trim()); - await dialog.hide(); - showToast(context, "Recovery successful!"); - await Navigator.of(context).pushReplacement( - MaterialPageRoute( - builder: (BuildContext context) { - return PopScope( - canPop: false, - child: PasswordEntryPage( - Configuration.instance, - PasswordEntryMode.reset, - const HomePage(), - ), - ); - }, - ), - ); - } catch (e) { - await dialog.hide(); - String errMessage = 'The recovery key you entered is incorrect'; - if (e is AssertionError) { - errMessage = '$errMessage : ${e.message}'; - } - await showErrorDialog(context, "Incorrect recovery key", errMessage); - } - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - floatingActionButton: DynamicFAB( - isKeypadOpen: isKeypadOpen, - isFormValid: _recoveryKey.text.isNotEmpty, - buttonText: 'Recover', - onPressedFunction: onPressed, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - body: Column( - children: [ - Expanded( - child: ListView( - children: [ - Padding( - padding: - const EdgeInsets.symmetric(vertical: 30, horizontal: 20), - child: Text( - context.l10n.forgotPassword, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: TextFormField( - decoration: InputDecoration( - filled: true, - hintText: "Enter your recovery key", - contentPadding: const EdgeInsets.all(20), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - ), - style: const TextStyle( - fontSize: 14, - fontFeatures: [FontFeature.tabularFigures()], - ), - controller: _recoveryKey, - autofocus: false, - autocorrect: false, - keyboardType: TextInputType.multiline, - maxLines: null, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 18), - child: Divider( - thickness: 1, - ), - ), - Row( - children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () { - showErrorDialog( - context, - "Sorry", - "Due to the nature of our end-to-end encryption protocol, your data cannot be decrypted without your password or recovery key", - ); - }, - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 20), - child: Center( - child: Text( - context.l10n.noRecoveryKeyTitle, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontSize: 14, - decoration: TextDecoration.underline, - ), - ), - ), - ), - ), - ], - ), - ], - ), - ), - ], - ), - ); - } -} diff --git a/mobile/apps/auth/lib/ui/account/request_pwd_verification_page.dart b/mobile/apps/auth/lib/ui/account/request_pwd_verification_page.dart deleted file mode 100644 index 5901d3bd45..0000000000 --- a/mobile/apps/auth/lib/ui/account/request_pwd_verification_page.dart +++ /dev/null @@ -1,219 +0,0 @@ -import "dart:convert"; -import "dart:typed_data"; - -import 'package:ente_auth/core/configuration.dart'; -import "package:ente_auth/l10n/l10n.dart"; -import "package:ente_auth/theme/ente_theme.dart"; -import 'package:ente_auth/ui/common/dynamic_fab.dart'; -import "package:ente_auth/utils/dialog_util.dart"; -import 'package:ente_crypto_dart/ente_crypto_dart.dart'; -import 'package:flutter/material.dart'; -import "package:logging/logging.dart"; - -typedef OnPasswordVerifiedFn = Future Function(Uint8List bytes); - -class RequestPasswordVerificationPage extends StatefulWidget { - final OnPasswordVerifiedFn onPasswordVerified; - final Function? onPasswordError; - - const RequestPasswordVerificationPage({ - super.key, - required this.onPasswordVerified, - this.onPasswordError, - }); - - @override - State createState() => - _RequestPasswordVerificationPageState(); -} - -class _RequestPasswordVerificationPageState - extends State { - final _logger = Logger((_RequestPasswordVerificationPageState).toString()); - final _passwordController = TextEditingController(); - final FocusNode _passwordFocusNode = FocusNode(); - String? email; - bool _passwordInFocus = false; - bool _passwordVisible = false; - - @override - void initState() { - super.initState(); - email = Configuration.instance.getEmail(); - _passwordFocusNode.addListener(() { - setState(() { - _passwordInFocus = _passwordFocusNode.hasFocus; - }); - }); - } - - @override - Widget build(BuildContext context) { - final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - FloatingActionButtonLocation? fabLocation() { - if (isKeypadOpen) { - return null; - } else { - return FloatingActionButtonLocation.centerFloat; - } - } - - return Scaffold( - resizeToAvoidBottomInset: isKeypadOpen, - appBar: AppBar( - elevation: 0, - leading: IconButton( - icon: const Icon(Icons.arrow_back), - color: Theme.of(context).iconTheme.color, - onPressed: () { - Navigator.of(context).pop(); - }, - ), - ), - body: _getBody(), - floatingActionButton: DynamicFAB( - key: const ValueKey("verifyPasswordButton"), - isKeypadOpen: isKeypadOpen, - isFormValid: _passwordController.text.isNotEmpty, - buttonText: context.l10n.verifyPassword, - onPressedFunction: () async { - FocusScope.of(context).unfocus(); - final dialog = createProgressDialog(context, context.l10n.pleaseWait); - await dialog.show(); - try { - final attributes = Configuration.instance.getKeyAttributes()!; - final Uint8List keyEncryptionKey = await CryptoUtil.deriveKey( - utf8.encode(_passwordController.text), - CryptoUtil.base642bin(attributes.kekSalt), - attributes.memLimit, - attributes.opsLimit, - ); - CryptoUtil.decryptSync( - CryptoUtil.base642bin(attributes.encryptedKey), - keyEncryptionKey, - CryptoUtil.base642bin(attributes.keyDecryptionNonce), - ); - await dialog.show(); - // pop - await widget.onPasswordVerified(keyEncryptionKey); - await dialog.hide(); - Navigator.of(context).pop(true); - } catch (e, s) { - _logger.severe("Error while verifying password", e, s); - await dialog.hide(); - if (widget.onPasswordError != null) { - widget.onPasswordError!(); - } else { - // ignore: unawaited_futures - showErrorDialog( - context, - context.l10n.incorrectPasswordTitle, - context.l10n.pleaseTryAgain, - ); - } - } - }, - ), - floatingActionButtonLocation: fabLocation(), - floatingActionButtonAnimator: NoScalingAnimation(), - ); - } - - Widget _getBody() { - return Column( - children: [ - Expanded( - child: AutofillGroup( - child: ListView( - children: [ - Padding( - padding: const EdgeInsets.only(top: 30, left: 20, right: 20), - child: Text( - context.l10n.enterPassword, - style: Theme.of(context).textTheme.headlineMedium, - ), - ), - Padding( - padding: const EdgeInsets.only( - bottom: 30, - left: 22, - right: 20, - ), - child: Text( - email ?? '', - style: getEnteTextTheme(context).smallMuted, - ), - ), - Visibility( - // hidden textForm for suggesting auto-fill service for saving - // password - visible: false, - child: TextFormField( - autofillHints: const [ - AutofillHints.email, - ], - autocorrect: false, - keyboardType: TextInputType.emailAddress, - initialValue: email, - textInputAction: TextInputAction.next, - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(20, 24, 20, 0), - child: TextFormField( - key: const ValueKey("passwordInputField"), - autofillHints: const [AutofillHints.password], - decoration: InputDecoration( - hintText: context.l10n.enterYourPasswordHint, - filled: true, - contentPadding: const EdgeInsets.all(20), - border: UnderlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(6), - ), - suffixIcon: _passwordInFocus - ? IconButton( - icon: Icon( - _passwordVisible - ? Icons.visibility - : Icons.visibility_off, - color: Theme.of(context).iconTheme.color, - size: 20, - ), - onPressed: () { - setState(() { - _passwordVisible = !_passwordVisible; - }); - }, - ) - : null, - ), - style: const TextStyle( - fontSize: 14, - ), - controller: _passwordController, - autofocus: true, - autocorrect: false, - obscureText: !_passwordVisible, - keyboardType: TextInputType.visiblePassword, - focusNode: _passwordFocusNode, - onChanged: (_) { - setState(() {}); - }, - ), - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 18), - child: Divider( - thickness: 1, - ), - ), - ], - ), - ), - ), - ], - ); - } -} diff --git a/mobile/apps/auth/lib/ui/home_page.dart b/mobile/apps/auth/lib/ui/home_page.dart index f6874882d7..32c8c2683f 100644 --- a/mobile/apps/auth/lib/ui/home_page.dart +++ b/mobile/apps/auth/lib/ui/home_page.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:app_links/app_links.dart'; import 'package:collection/collection.dart'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; import 'package:ente_auth/events/codes_updated_event.dart'; @@ -14,7 +15,6 @@ import 'package:ente_auth/onboarding/model/tag_enums.dart'; import 'package:ente_auth/onboarding/view/common/tag_chip.dart'; import 'package:ente_auth/onboarding/view/setup_enter_secret_key_page.dart'; import 'package:ente_auth/services/preference_service.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/store/code_display_store.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/ente_theme.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart index e72585e6de..46d284ca47 100644 --- a/mobile/apps/auth/lib/ui/settings/account_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/account_section_widget.dart @@ -1,11 +1,11 @@ import 'package:ente_accounts/pages/change_email_dialog.dart'; import 'package:ente_accounts/pages/delete_account_page.dart'; import 'package:ente_accounts/pages/password_entry_page.dart'; +import 'package:ente_accounts/pages/recovery_key_page.dart'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/account/recovery_key_page.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; import 'package:ente_auth/ui/components/menu_item_widget.dart'; @@ -124,6 +124,7 @@ class AccountSectionWidget extends StatelessWidget { routeToPage( context, RecoveryKeyPage( + Configuration.instance, recoveryKey, l10n.ok, showAppBar: true, diff --git a/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart b/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart index 6c5bcd168d..83162b04f8 100644 --- a/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/notification_banner_widget.dart @@ -1,10 +1,10 @@ import 'dart:io'; import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; -import 'package:ente_auth/l10n/l10n.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/services/preference_service.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/ui/components/banner_widget.dart'; import 'package:flutter/material.dart'; diff --git a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart index 9fa7d06b54..7406a9fd94 100644 --- a/mobile/apps/auth/lib/ui/settings/security_section_widget.dart +++ b/mobile/apps/auth/lib/ui/settings/security_section_widget.dart @@ -2,13 +2,13 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:ente_accounts/models/user_details.dart'; +import 'package:ente_accounts/pages/request_pwd_verification_page.dart'; import 'package:ente_accounts/pages/sessions_page.dart'; +import 'package:ente_accounts/services/passkey_service.dart'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; -import 'package:ente_auth/services/passkey_service.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/theme/ente_theme.dart'; -import 'package:ente_auth/ui/account/request_pwd_verification_page.dart'; import 'package:ente_auth/ui/components/buttons/button_widget.dart'; import 'package:ente_auth/ui/components/captioned_text_widget.dart'; import 'package:ente_auth/ui/components/expandable_menu_item_widget.dart'; @@ -243,6 +243,7 @@ class _SecuritySectionWidgetState extends State { await routeToPage( context, RequestPasswordVerificationPage( + Configuration.instance, onPasswordVerified: (Uint8List keyEncryptionKey) async { final Uint8List loginKey = await CryptoUtil.deriveLoginKey(keyEncryptionKey); diff --git a/mobile/apps/auth/lib/ui/settings_page.dart b/mobile/apps/auth/lib/ui/settings_page.dart index d543985b57..d1965eb009 100644 --- a/mobile/apps/auth/lib/ui/settings_page.dart +++ b/mobile/apps/auth/lib/ui/settings_page.dart @@ -1,9 +1,9 @@ import 'dart:io'; +import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/onboarding/view/onboarding_page.dart'; -import 'package:ente_auth/services/user_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/colors.dart'; import 'package:ente_auth/theme/ente_theme.dart'; From a9c8e4476f5627b20119f295677e997787957764 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 11:58:22 +0530 Subject: [PATCH 114/164] Remove old password reentry, recovery key, recovery, and password verification pages; refactor imports and update references in settings and home page. From e6a867a85935a38dbf4ed5dc89fb7b65cbf424e9 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 1 Aug 2025 12:02:26 +0530 Subject: [PATCH 115/164] Cleanup --- mobile/apps/auth/lib/models/api/user/srp.dart | 135 ------------------ 1 file changed, 135 deletions(-) delete mode 100644 mobile/apps/auth/lib/models/api/user/srp.dart diff --git a/mobile/apps/auth/lib/models/api/user/srp.dart b/mobile/apps/auth/lib/models/api/user/srp.dart deleted file mode 100644 index baee0335ff..0000000000 --- a/mobile/apps/auth/lib/models/api/user/srp.dart +++ /dev/null @@ -1,135 +0,0 @@ -class SetupSRPRequest { - final String srpUserID; - final String srpSalt; - final String srpVerifier; - final String srpA; - final bool isUpdate; - - SetupSRPRequest({ - required this.srpUserID, - required this.srpSalt, - required this.srpVerifier, - required this.srpA, - required this.isUpdate, - }); - - Map toMap() { - return { - 'srpUserID': srpUserID.toString(), - 'srpSalt': srpSalt, - 'srpVerifier': srpVerifier, - 'srpA': srpA, - 'isUpdate': isUpdate, - }; - } - - factory SetupSRPRequest.fromJson(Map json) { - return SetupSRPRequest( - srpUserID: json['srpUserID'], - srpSalt: json['srpSalt'], - srpVerifier: json['srpVerifier'], - srpA: json['srpA'], - isUpdate: json['isUpdate'], - ); - } -} - -class SetupSRPResponse { - final String setupID; - final String srpB; - - SetupSRPResponse({ - required this.setupID, - required this.srpB, - }); - - Map toMap() { - return { - 'setupID': setupID.toString(), - 'srpB': srpB, - }; - } - - factory SetupSRPResponse.fromJson(Map json) { - return SetupSRPResponse( - setupID: json['setupID'], - srpB: json['srpB'], - ); - } -} - -class CompleteSRPSetupRequest { - final String setupID; - final String srpM1; - - CompleteSRPSetupRequest({ - required this.setupID, - required this.srpM1, - }); - - Map toMap() { - return { - 'setupID': setupID.toString(), - 'srpM1': srpM1, - }; - } - - factory CompleteSRPSetupRequest.fromJson(Map json) { - return CompleteSRPSetupRequest( - setupID: json['setupID'], - srpM1: json['srpM1'], - ); - } -} -class SrpAttributes { - final String srpUserID; - final String srpSalt; - final int memLimit; - final int opsLimit; - final String kekSalt; - final bool isEmailMFAEnabled; - - SrpAttributes({ - required this.srpUserID, - required this.srpSalt, - required this.memLimit, - required this.opsLimit, - required this.kekSalt, - required this.isEmailMFAEnabled, - }); - - factory SrpAttributes.fromMap(Map map) { - return SrpAttributes( - srpUserID: map['attributes']['srpUserID'], - srpSalt: map['attributes']['srpSalt'], - memLimit: map['attributes']['memLimit'], - opsLimit: map['attributes']['opsLimit'], - kekSalt: map['attributes']['kekSalt'], - isEmailMFAEnabled: map['attributes']['isEmailMFAEnabled'], - ); - } -} - -class CompleteSRPSetupResponse { - final String setupID; - final String srpM2; - - CompleteSRPSetupResponse({ - required this.setupID, - required this.srpM2, - }); - - Map toMap() { - return { - 'setupID': setupID, - 'srpM2': srpM2, - }; - } - - factory CompleteSRPSetupResponse.fromJson(Map json) { - return CompleteSRPSetupResponse( - setupID: json['setupID'], - srpM2: json['srpM2'], - ); - } -} From d7a7144b33ef2fc4522c50c7e81f7797e1c37c38 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Mon, 4 Aug 2025 19:56:45 +0530 Subject: [PATCH 116/164] Fix android build --- mobile/apps/auth/lib/app/view/app.dart | 2 +- mobile/apps/auth/lib/ente_theme_data.dart | 2 +- mobile/apps/auth/lib/l10n/l10n.dart | 2 +- mobile/apps/auth/lib/main.dart | 2 +- .../auth/lib/ui/home/coach_mark_widget.dart | 3 +- .../apps/auth/lib/ui/scanner_gauth_page.dart | 2 +- mobile/apps/auth/lib/ui/scanner_page.dart | 2 +- .../ui/settings/data/duplicate_code_page.dart | 2 +- mobile/apps/auth/pubspec.lock | 115 +++++++----- mobile/apps/auth/pubspec.yaml | 9 +- .../accounts/lib/pages/email_entry_page.dart | 2 +- .../accounts/lib/pages/login_page.dart | 5 +- .../accounts/lib/pages/recovery_key_page.dart | 15 +- mobile/packages/accounts/pubspec.lock | 174 ++++++++++-------- mobile/packages/accounts/pubspec.yaml | 4 +- mobile/packages/base/pubspec.lock | 80 ++++---- .../packages/configuration/.flutter-plugins | 21 +++ mobile/packages/configuration/pubspec.lock | 114 ++++++------ mobile/packages/events/pubspec.lock | 80 ++++---- mobile/packages/lock_screen/pubspec.lock | 174 ++++++++++-------- mobile/packages/lock_screen/pubspec.yaml | 2 +- mobile/packages/logging/.flutter-plugins | 14 ++ mobile/packages/logging/pubspec.lock | 106 +++++------ mobile/packages/logging/pubspec.yaml | 2 +- mobile/packages/network/.flutter-plugins | 26 +++ mobile/packages/network/pubspec.lock | 114 ++++++------ mobile/packages/script.sh | 13 ++ .../.filecache | 1 + .../gen_l10n_inputs_and_outputs.json | 1 + .../gen_localizations.d | 1 + .../gen_localizations.stamp | 1 + .../outputs.json | 1 + mobile/packages/strings/pubspec.lock | 84 ++++----- .../ui/lib/components/menu_item_widget.dart | 5 +- .../ui/lib/theme/ente_theme_data.dart | 4 +- mobile/packages/ui/pubspec.lock | 146 ++++++++------- mobile/packages/ui/pubspec.yaml | 3 +- mobile/packages/utils/lib/platform_util.dart | 4 +- mobile/packages/utils/pubspec.lock | 146 ++++++++------- mobile/packages/utils/pubspec.yaml | 10 +- 40 files changed, 830 insertions(+), 664 deletions(-) create mode 100644 mobile/packages/configuration/.flutter-plugins create mode 100644 mobile/packages/logging/.flutter-plugins create mode 100644 mobile/packages/network/.flutter-plugins create mode 100755 mobile/packages/script.sh create mode 100644 mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/.filecache create mode 100644 mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_l10n_inputs_and_outputs.json create mode 100644 mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.d create mode 100644 mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.stamp create mode 100644 mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/outputs.json diff --git a/mobile/apps/auth/lib/app/view/app.dart b/mobile/apps/auth/lib/app/view/app.dart index 833e502975..81dedefa5f 100644 --- a/mobile/apps/auth/lib/app/view/app.dart +++ b/mobile/apps/auth/lib/app/view/app.dart @@ -5,7 +5,7 @@ import 'package:adaptive_theme/adaptive_theme.dart'; import 'package:ente_accounts/services/user_service.dart'; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/ente_theme_data.dart'; -import "package:ente_auth/l10n/l10n.dart"; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/locale.dart'; import "package:ente_auth/onboarding/view/onboarding_page.dart"; import 'package:ente_auth/services/authenticator_service.dart'; diff --git a/mobile/apps/auth/lib/ente_theme_data.dart b/mobile/apps/auth/lib/ente_theme_data.dart index 954b7e22a8..9b886657d3 100644 --- a/mobile/apps/auth/lib/ente_theme_data.dart +++ b/mobile/apps/auth/lib/ente_theme_data.dart @@ -38,7 +38,7 @@ final lightThemeData = ThemeData( bodyMedium: const TextStyle(color: Colors.yellow), bodyLarge: const TextStyle(color: Colors.orange), ), - cardColor: const Color.fromRGBO(250, 250, 250, 1.0), + cardColor: const Color.fromRGBO(250, 250, 250, 1), dialogTheme: const DialogTheme().copyWith( backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), // titleTextStyle: const TextStyle( diff --git a/mobile/apps/auth/lib/l10n/l10n.dart b/mobile/apps/auth/lib/l10n/l10n.dart index 92a67a576e..7f1dd185f2 100644 --- a/mobile/apps/auth/lib/l10n/l10n.dart +++ b/mobile/apps/auth/lib/l10n/l10n.dart @@ -5,4 +5,4 @@ export "package:flutter_gen/gen_l10n/app_localizations.dart"; extension AppLocalizationsX on BuildContext { AppLocalizations get l10n => AppLocalizations.of(this); -} +} \ No newline at end of file diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 52ad328bbe..5676f508eb 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -7,7 +7,7 @@ import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/arb/app_localizations.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/billing_service.dart'; diff --git a/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart b/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart index 9583db2757..aa1f8c9166 100644 --- a/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart +++ b/mobile/apps/auth/lib/ui/home/coach_mark_widget.dart @@ -23,7 +23,8 @@ class CoachMarkWidget extends StatelessWidget { Expanded( child: Container( width: double.infinity, - color: Theme.of(context).colorScheme.surface.withOpacity(0.1), + color: + Theme.of(context).colorScheme.surface.withOpacity(0.1), child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8), child: Row( diff --git a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart index 41d0a762dd..58e6da3b41 100644 --- a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart @@ -6,7 +6,7 @@ import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/settings/data/import/google_auth_import.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; class ScannerGoogleAuthPage extends StatefulWidget { const ScannerGoogleAuthPage({super.key}); diff --git a/mobile/apps/auth/lib/ui/scanner_page.dart b/mobile/apps/auth/lib/ui/scanner_page.dart index a0f88b7c87..ea9dfa3f15 100644 --- a/mobile/apps/auth/lib/ui/scanner_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_page.dart @@ -4,7 +4,7 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; class ScannerPage extends StatefulWidget { const ScannerPage({super.key}); diff --git a/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart b/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart index 0aba15ed34..93600ebff1 100644 --- a/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart +++ b/mobile/apps/auth/lib/ui/settings/data/duplicate_code_page.dart @@ -1,6 +1,6 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; -import 'package:ente_auth/services/deduplication_service.dart'; +import 'package:ente_auth/services/deduplication_service.dart'; import 'package:ente_auth/store/code_store.dart'; import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/code_widget.dart'; diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 8644a30981..7ab765cd10 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -354,10 +354,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.1" + version: "7.0.2" dio: dependency: "direct main" description: @@ -731,18 +731,18 @@ packages: dependency: "direct main" description: name: flutter_native_splash - sha256: aa06fec78de2190f3db4319dd60fdc8d12b2626e93ef9828633928c2dcaea840 + sha256: "7062602e0dbd29141fb8eb19220b5871ca650be5197ab9c1f193a28b17537bc7" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.4.4" flutter_plugin_android_lifecycle: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda" + sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf" url: "https://pub.dev" source: hosted - version: "2.0.22" + version: "2.0.26" flutter_secure_storage: dependency: "direct main" description: @@ -819,10 +819,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.dev" source: hosted - version: "2.0.16" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -1013,10 +1013,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: ea1432d167339ea9b5bb153f0571d0039607a873d6e04e0117af043f14a1fd4b + sha256: c2fcb3920cf2b6ae6845954186420fca40bc0a8abcc84903b7801f17d7050d7c url: "https://pub.dev" source: hosted - version: "6.8.0" + version: "6.9.0" leak_tracker: dependency: transitive description: @@ -1061,18 +1061,18 @@ packages: dependency: "direct main" description: name: local_auth_android - sha256: "5351c7eea8823de28e37d8b7b3e386d944b80f2a77edb91a5707fb97a41fc1b1" + sha256: "8bba79f4f0f7bc812fce2ca20915d15618c37721246ba6c3ef2aa7a763a90cf2" url: "https://pub.dev" source: hosted - version: "1.0.45" + version: "1.0.47" local_auth_darwin: dependency: "direct main" description: name: local_auth_darwin - sha256: "6d2950da311d26d492a89aeb247c72b4653ddc93601ea36a84924a396806d49c" + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.4.3" local_auth_platform_interface: dependency: transitive description: @@ -1277,10 +1277,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7" + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.10" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -1397,10 +1397,10 @@ packages: dependency: transitive description: name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 + sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" qr: dependency: transitive description: @@ -1409,14 +1409,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - qr_code_scanner: + qr_code_scanner_plus: dependency: "direct main" description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd + name: qr_code_scanner_plus + sha256: "39696b50d277097ee4d90d4292de36f38c66213a4f5216a06b2bdd2b63117859" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.10+1" qr_flutter: dependency: "direct main" description: @@ -1477,10 +1477,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -1533,10 +1533,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" shortid: dependency: transitive description: @@ -1578,10 +1578,10 @@ packages: dependency: transitive description: name: source_helper - sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" url: "https://pub.dev" source: hosted - version: "1.3.4" + version: "1.3.5" source_span: dependency: transitive description: @@ -1601,28 +1601,51 @@ packages: sqflite: dependency: "direct main" description: - path: sqflite - ref: HEAD - resolved-ref: "699aaafa282d823b89ca568aac7a68d2c29ddab6" - url: "https://github.com/tekartik/sqflite" - source: git - version: "2.3.3+2" + name: sqflite + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" sqflite_common: dependency: transitive description: name: sqflite_common - sha256: "2d8e607db72e9cb7748c9c6e739e2c9618320a5517de693d5a24609c4671b1a4" + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" url: "https://pub.dev" source: hosted - version: "2.5.4+4" + version: "2.5.4+6" sqflite_common_ffi: dependency: "direct main" description: name: sqflite_common_ffi - sha256: a6057d4c87e9260ba1ec436ebac24760a110589b9c0a859e128842eb69a7ef04 + sha256: "883dd810b2b49e6e8c3b980df1829ef550a94e3f87deab5d864917d27ca6bf36" url: "https://pub.dev" source: hosted - version: "2.3.3+1" + version: "2.3.4+4" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "22adfd9a2c7d634041e96d6241e6e1c8138ca6817018afc5d443fef91dcefa9c" + url: "https://pub.dev" + source: hosted + version: "2.4.1+1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" sqlite3: dependency: "direct main" description: @@ -1795,10 +1818,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: a4e5f34f2fadf1fa7b4e69db89189056e313c9c98e8ad420e6b53677b6abc334 + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.3.11" + version: "6.3.14" url_launcher_ios: dependency: "direct main" description: @@ -1859,10 +1882,10 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "27d5fefe86fb9aace4a9f8375b56b3c292b64d8c04510df230f849850d912cb7" + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" url: "https://pub.dev" source: hosted - version: "1.1.15" + version: "1.1.18" vector_graphics_codec: dependency: transitive description: @@ -1931,10 +1954,10 @@ packages: dependency: "direct main" description: name: win32 - sha256: "4d45dc9069dba4619dc0ebd93c7cec5e66d8482cb625a370ac806dcc8165f2ec" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.5.5" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -1947,10 +1970,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: ab8b2a7f97543d3db2b506c9d875e637149d48ee0c6a5cb5f5fd6e0dac463792 + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.dev" source: hosted - version: "0.4.2" + version: "0.4.3" xdg_directories: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 48f946d3f6..1ff8e357f0 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -58,7 +58,7 @@ dependencies: sdk: flutter flutter_animate: ^4.1.0 flutter_bloc: ^8.0.1 - flutter_context_menu: ^0.2.0 + flutter_context_menu: 0.2.0 flutter_displaymode: ^0.6.0 flutter_email_sender: ^6.0.2 # revert to pub.dev when merged @@ -104,16 +104,13 @@ dependencies: pointycastle: ^3.7.3 privacy_screen: ^0.0.6 protobuf: ^3.0.0 - qr_code_scanner: ^1.0.1 + qr_code_scanner_plus: ^2.0.10+1 qr_flutter: ^4.1.0 sentry: ^8.14.2 sentry_flutter: ^8.14.2 share_plus: ^10.0.2 shared_preferences: ^2.0.5 - sqflite: - git: - url: https://github.com/tekartik/sqflite - path: sqflite + sqflite: ^2.4.1 sqflite_common_ffi: ^2.3.0+4 sqlite3: ^2.4.3 sqlite3_flutter_libs: ^0.5.24 diff --git a/mobile/packages/accounts/lib/pages/email_entry_page.dart b/mobile/packages/accounts/lib/pages/email_entry_page.dart index 7c922d793d..62c49e2922 100644 --- a/mobile/packages/accounts/lib/pages/email_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/email_entry_page.dart @@ -8,7 +8,7 @@ import 'package:ente_utils/platform_util.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:password_strength/password_strength.dart'; -import 'package:step_progress_indicator/step_progress_indicator.dart'; +import "package:step_progress_indicator/step_progress_indicator.dart"; import "package:styled_text/styled_text.dart"; class EmailEntryPage extends StatefulWidget { diff --git a/mobile/packages/accounts/lib/pages/login_page.dart b/mobile/packages/accounts/lib/pages/login_page.dart index c51b74e8a4..3caeebea2c 100644 --- a/mobile/packages/accounts/lib/pages/login_page.dart +++ b/mobile/packages/accounts/lib/pages/login_page.dart @@ -1,4 +1,4 @@ -import 'package:email_validator/email_validator.dart'; +import "package:email_validator/email_validator.dart"; import 'package:ente_accounts/ente_accounts.dart'; import 'package:ente_accounts/models/errors.dart'; import 'package:ente_configuration/base_configuration.dart'; @@ -8,7 +8,8 @@ import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import "package:styled_text/styled_text.dart"; +import "package:styled_text/tags/styled_text_tag_action.dart"; +import "package:styled_text/widgets/styled_text.dart"; class LoginPage extends StatefulWidget { final BaseConfiguration config; diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index 3cf2249771..a4ad8cb4aa 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -152,17 +152,16 @@ class _RecoveryKeyPageState extends State { getEnteColorScheme(context).primary500, getEnteColorScheme(context).primary300, ], - stops: [0.0, 0.9753], + stops: const [0.0, 0.9753], ), ), child: DottedBorder( - options: const RoundedRectDottedBorderOptions( - padding: EdgeInsets.zero, - strokeWidth: 1, - color: Color(0xFF6B6B6B), - dashPattern: [6, 6], - radius: Radius.circular(8), - ), + padding: EdgeInsets.zero, + strokeWidth: 1, + color: const Color(0xFF6B6B6B), + dashPattern: const [6, 6], + borderType: BorderType.RRect, + radius: const Radius.circular(8), child: SizedBox( width: double.infinity, child: Stack( diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index bca81b2cd0..9e7b4bcd4f 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: "direct main" description: @@ -69,34 +69,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: "direct main" description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" dio: dependency: "direct main" description: @@ -181,10 +181,10 @@ packages: dependency: "direct main" description: name: dotted_border - sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "2.1.0" email_validator: dependency: "direct main" description: @@ -273,22 +273,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + expandable: + dependency: transitive + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -301,10 +309,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.2.14" fixnum: dependency: transitive description: @@ -330,10 +338,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "6.0.3" flutter_inappwebview: dependency: transitive description: @@ -431,10 +439,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf" url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.26" flutter_secure_storage: dependency: transitive description: @@ -553,10 +561,10 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" http_profile: dependency: transitive description: @@ -569,10 +577,10 @@ packages: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" jni: dependency: transitive description: @@ -601,18 +609,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -625,10 +633,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" local_auth: dependency: transitive description: @@ -641,18 +649,18 @@ packages: dependency: transitive description: name: local_auth_android - sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + sha256: "8bba79f4f0f7bc812fce2ca20915d15618c37721246ba6c3ef2aa7a763a90cf2" url: "https://pub.dev" source: hosted - version: "1.0.50" + version: "1.0.47" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.4.3" local_auth_platform_interface: dependency: transitive description: @@ -681,10 +689,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -697,10 +705,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: transitive description: @@ -769,10 +777,26 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" + path_drawing: + dependency: transitive + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" path_provider: dependency: transitive description: @@ -785,10 +809,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -961,10 +985,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -1009,7 +1033,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -1030,10 +1054,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -1046,10 +1070,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" step_progress_indicator: dependency: "direct main" description: @@ -1062,18 +1086,18 @@ packages: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" styled_text: dependency: "direct main" description: @@ -1086,26 +1110,26 @@ packages: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: transitive description: @@ -1142,18 +1166,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.14" url_launcher_ios: dependency: "direct main" description: @@ -1190,10 +1214,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -1222,10 +1246,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -1246,10 +1270,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -1283,5 +1307,5 @@ packages: source: hosted version: "1.1.1" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index 1d4064ac75..e787f25add 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -11,7 +11,7 @@ dependencies: bip39: ^1.0.6 collection: ^1.18.0 dio: ^5.4.0 - dotted_border: ^3.1.0 + dotted_border: ^2.0.0+2 email_validator: ^3.0.0 ente_base: path: ../base @@ -32,7 +32,7 @@ dependencies: path: ../ui ente_utils: path: ../utils - file_saver: ^0.3.0 + file_saver: ^0.2.14 flutter: sdk: flutter logging: ^1.2.0 diff --git a/mobile/packages/base/pubspec.lock b/mobile/packages/base/pubspec.lock index 6bbc3fcd93..41d1dd2e93 100644 --- a/mobile/packages/base/pubspec.lock +++ b/mobile/packages/base/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -71,18 +71,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -95,18 +95,18 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -119,71 +119,71 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" vector_math: dependency: transitive description: @@ -196,10 +196,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/configuration/.flutter-plugins b/mobile/packages/configuration/.flutter-plugins new file mode 100644 index 0000000000..a788946466 --- /dev/null +++ b/mobile/packages/configuration/.flutter-plugins @@ -0,0 +1,21 @@ +# This is a generated file; do not edit or check into version control. +device_info_plus=/Users/amanraj/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/ +flutter_secure_storage=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/ +flutter_secure_storage_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/ +flutter_secure_storage_macos=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/ +flutter_secure_storage_web=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/ +flutter_secure_storage_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/ +package_info_plus=/Users/amanraj/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/ +path_provider=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider-2.1.5/ +path_provider_android=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/ +path_provider_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/ +path_provider_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ +path_provider_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/ +sentry_flutter=/Users/amanraj/.pub-cache/hosted/pub.dev/sentry_flutter-8.14.2/ +shared_preferences=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences-2.5.3/ +shared_preferences_android=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.7/ +shared_preferences_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/ +shared_preferences_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/ +shared_preferences_web=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/ +shared_preferences_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/ +sodium_libs=/Users/amanraj/.pub-cache/hosted/pub.dev/sodium_libs-2.2.1+6/ diff --git a/mobile/packages/configuration/pubspec.lock b/mobile/packages/configuration/pubspec.lock index 11868bfed4..8d3b2fb890 100644 --- a/mobile/packages/configuration/pubspec.lock +++ b/mobile/packages/configuration/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: "direct main" description: @@ -29,34 +29,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -93,10 +93,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" ente_base: dependency: "direct main" description: @@ -139,18 +139,18 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -274,18 +274,18 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" intl: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" js: dependency: transitive description: @@ -306,18 +306,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -330,10 +330,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: transitive description: @@ -346,10 +346,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -362,10 +362,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" package_info_plus: dependency: transitive description: @@ -386,10 +386,10 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: "direct main" description: @@ -402,10 +402,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -490,10 +490,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -538,7 +538,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -559,10 +559,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -575,50 +575,50 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: "direct main" description: @@ -655,10 +655,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -671,10 +671,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -692,5 +692,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/events/pubspec.lock b/mobile/packages/events/pubspec.lock index 2ee3344a78..c929347040 100644 --- a/mobile/packages/events/pubspec.lock +++ b/mobile/packages/events/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" event_bus: dependency: "direct main" description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -79,18 +79,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -103,18 +103,18 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -127,71 +127,71 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" vector_math: dependency: transitive description: @@ -204,10 +204,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index 40cf271ac0..ec4bcab64c 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: transitive description: @@ -69,34 +69,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" dio: dependency: transitive description: @@ -181,10 +181,10 @@ packages: dependency: transitive description: name: dotted_border - sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + sha256: "108837e11848ca776c53b30bc870086f84b62ed6e01c503ed976e8f8c7df9c04" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "2.1.0" email_validator: dependency: transitive description: @@ -273,22 +273,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + expandable: + dependency: transitive + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -301,10 +309,10 @@ packages: dependency: transitive description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.2.14" fixnum: dependency: transitive description: @@ -330,10 +338,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "6.0.3" flutter_inappwebview: dependency: transitive description: @@ -431,10 +439,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e + sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf" url: "https://pub.dev" source: hosted - version: "2.0.28" + version: "2.0.26" flutter_secure_storage: dependency: "direct main" description: @@ -553,10 +561,10 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" http_profile: dependency: transitive description: @@ -569,10 +577,10 @@ packages: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" jni: dependency: transitive description: @@ -601,18 +609,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -625,10 +633,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" local_auth: dependency: "direct main" description: @@ -641,18 +649,18 @@ packages: dependency: transitive description: name: local_auth_android - sha256: "82b2bdeee2199a510d3b7716121e96a6609da86693bb0863edd8566355406b79" + sha256: "8bba79f4f0f7bc812fce2ca20915d15618c37721246ba6c3ef2aa7a763a90cf2" url: "https://pub.dev" source: hosted - version: "1.0.50" + version: "1.0.47" local_auth_darwin: dependency: transitive description: name: local_auth_darwin - sha256: "25163ce60a5a6c468cf7a0e3dc8a165f824cabc2aa9e39a5e9fc5c2311b7686f" + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.4.3" local_auth_platform_interface: dependency: transitive description: @@ -681,10 +689,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -697,10 +705,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: transitive description: @@ -769,10 +777,26 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" + path_drawing: + dependency: transitive + description: + name: path_drawing + sha256: bbb1934c0cbb03091af082a6389ca2080345291ef07a5fa6d6e078ba8682f977 + url: "https://pub.dev" + source: hosted + version: "1.0.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" path_provider: dependency: transitive description: @@ -785,10 +809,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -961,10 +985,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -1009,7 +1033,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -1030,10 +1054,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -1046,10 +1070,10 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" step_progress_indicator: dependency: transitive description: @@ -1062,18 +1086,18 @@ packages: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" styled_text: dependency: transitive description: @@ -1086,26 +1110,26 @@ packages: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: transitive description: @@ -1142,18 +1166,18 @@ packages: dependency: transitive description: name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.14" url_launcher_ios: dependency: transitive description: @@ -1190,10 +1214,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -1222,10 +1246,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -1246,10 +1270,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -1283,5 +1307,5 @@ packages: source: hosted version: "1.1.1" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/lock_screen/pubspec.yaml b/mobile/packages/lock_screen/pubspec.yaml index 5f6f1529c1..cbe75137e2 100644 --- a/mobile/packages/lock_screen/pubspec.yaml +++ b/mobile/packages/lock_screen/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: url: https://github.com/eaceto/flutter_local_authentication ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 flutter_secure_storage: ^9.0.0 - local_auth: ^2.1.8 + local_auth: ^2.3.0 logging: ^1.1.1 pinput: ^5.0.0 privacy_screen: ^0.0.8 diff --git a/mobile/packages/logging/.flutter-plugins b/mobile/packages/logging/.flutter-plugins new file mode 100644 index 0000000000..9b8a33aaa7 --- /dev/null +++ b/mobile/packages/logging/.flutter-plugins @@ -0,0 +1,14 @@ +# This is a generated file; do not edit or check into version control. +package_info_plus=/Users/amanraj/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/ +path_provider=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider-2.1.5/ +path_provider_android=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/ +path_provider_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/ +path_provider_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ +path_provider_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/ +sentry_flutter=/Users/amanraj/.pub-cache/hosted/pub.dev/sentry_flutter-8.14.2/ +shared_preferences=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences-2.5.3/ +shared_preferences_android=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.7/ +shared_preferences_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/ +shared_preferences_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/ +shared_preferences_web=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/ +shared_preferences_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/ diff --git a/mobile/packages/logging/pubspec.lock b/mobile/packages/logging/pubspec.lock index 4dd0697bf7..2577b45ac5 100644 --- a/mobile/packages/logging/pubspec.lock +++ b/mobile/packages/logging/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" crypto: dependency: transitive description: @@ -53,18 +53,18 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -116,34 +116,34 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" intl: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -156,10 +156,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: "direct main" description: @@ -172,10 +172,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -188,10 +188,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" package_info_plus: dependency: "direct main" description: @@ -212,10 +212,10 @@ packages: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: "direct main" description: @@ -228,10 +228,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -308,10 +308,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -356,15 +356,15 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -377,42 +377,42 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" typed_data: dependency: transitive description: @@ -441,10 +441,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -457,10 +457,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" xdg_directories: dependency: transitive description: @@ -470,5 +470,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.27.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/logging/pubspec.yaml b/mobile/packages/logging/pubspec.yaml index aed0a718ed..646f7b3291 100644 --- a/mobile/packages/logging/pubspec.yaml +++ b/mobile/packages/logging/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: flutter: sdk: flutter http: ^1.4.0 - intl: ^0.20.2 + intl: ^0.19.0 logging: ^1.3.0 package_info_plus: ^8.3.0 path: ^1.9.0 diff --git a/mobile/packages/network/.flutter-plugins b/mobile/packages/network/.flutter-plugins new file mode 100644 index 0000000000..1cea5815b1 --- /dev/null +++ b/mobile/packages/network/.flutter-plugins @@ -0,0 +1,26 @@ +# This is a generated file; do not edit or check into version control. +cronet_http=/Users/amanraj/.pub-cache/hosted/pub.dev/cronet_http-1.4.0/ +cupertino_http=/Users/amanraj/.pub-cache/hosted/pub.dev/cupertino_http-2.2.0/ +device_info_plus=/Users/amanraj/.pub-cache/hosted/pub.dev/device_info_plus-9.1.2/ +flutter_secure_storage=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage-9.2.4/ +flutter_secure_storage_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_linux-1.2.3/ +flutter_secure_storage_macos=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_macos-3.1.3/ +flutter_secure_storage_web=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_web-1.2.1/ +flutter_secure_storage_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/flutter_secure_storage_windows-3.1.2/ +jni=/Users/amanraj/.pub-cache/hosted/pub.dev/jni-0.14.2/ +objective_c=/Users/amanraj/.pub-cache/hosted/pub.dev/objective_c-7.1.0/ +package_info_plus=/Users/amanraj/.pub-cache/hosted/pub.dev/package_info_plus-8.3.0/ +path_provider=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider-2.1.5/ +path_provider_android=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_android-2.2.15/ +path_provider_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_foundation-2.4.1/ +path_provider_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ +path_provider_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/ +sentry_flutter=/Users/amanraj/.pub-cache/hosted/pub.dev/sentry_flutter-8.14.2/ +shared_preferences=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences-2.5.3/ +shared_preferences_android=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.7/ +shared_preferences_foundation=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.4/ +shared_preferences_linux=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/ +shared_preferences_web=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.3/ +shared_preferences_windows=/Users/amanraj/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/ +sodium_libs=/Users/amanraj/.pub-cache/hosted/pub.dev/sodium_libs-2.2.1+6/ +ua_client_hints=/Users/amanraj/.pub-cache/hosted/pub.dev/ua_client_hints-1.4.1/ diff --git a/mobile/packages/network/pubspec.lock b/mobile/packages/network/pubspec.lock index 93c404e894..cec364d41e 100644 --- a/mobile/packages/network/pubspec.lock +++ b/mobile/packages/network/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: transitive description: @@ -29,34 +29,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" dio: dependency: "direct main" description: @@ -178,18 +178,18 @@ packages: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -313,10 +313,10 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" http_profile: dependency: transitive description: @@ -329,10 +329,10 @@ packages: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" jni: dependency: transitive description: @@ -361,18 +361,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -385,10 +385,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: transitive description: @@ -401,10 +401,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -417,10 +417,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" native_dio_adapter: dependency: "direct main" description: @@ -465,10 +465,10 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: transitive description: @@ -481,10 +481,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -577,10 +577,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -625,7 +625,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -646,10 +646,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -662,50 +662,50 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: transitive description: @@ -750,10 +750,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -774,10 +774,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -795,5 +795,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/script.sh b/mobile/packages/script.sh new file mode 100755 index 0000000000..5ae3f45ec6 --- /dev/null +++ b/mobile/packages/script.sh @@ -0,0 +1,13 @@ +// filepath: /Users/amanraj/development/ente/mobile/packages/script.sh +#!/bin/bash + +# Exit immediately if a command exits with a non-zero status. +set -e + +echo "🚀 Starting dependency fetch for all Flutter/Dart projects..." + +# Find all directories containing a pubspec.yaml and run 'flutter pub get' in them. +# This covers 'apps' and 'packages' directories. +find . -name "pubspec.yaml" -execdir flutter pub get \; + +echo "✅ All dependencies fetched successfully!" \ No newline at end of file diff --git a/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/.filecache b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/.filecache new file mode 100644 index 0000000000..07fb61f785 --- /dev/null +++ b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/.filecache @@ -0,0 +1 @@ +{"version":2,"files":[{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_zh.dart","hash":"7f7cc8995f142b71e60d866712f5cc79"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ka.arb","hash":"9cd3b583ef2a0ba24dce579b0e551f9c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hi.arb","hash":"9ed088070b8a4e1abc6ca573f571d461"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ka.dart","hash":"3efbe57fcc84fa800b418582502f4d8b"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_uk.dart","hash":"9616766d449b01fa1d5bbcec48678b8c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pl.dart","hash":"e706eba44092f662d3e682b6002c6b6e"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_tr.dart","hash":"a94045bf44762c3e6119b5aeb71765fe"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sk.dart","hash":"4bb0e6197457be6fac2ba4b61fcc6634"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_km.arb","hash":"23fb911d60d6124067af7c81c92d17d0"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_id.dart","hash":"ec966ec5ec5ca5cd0db226b4afbb5002"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ja.dart","hash":"b1089f8a4306c1483aa6c4fa71a94e09"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hi.dart","hash":"995674e33c2b7ccf4548eb486fbebcfa"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_be.arb","hash":"891655dc1aace6917ee027b4703e5038"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sl.dart","hash":"e393b9257b22b332702c09e6bc9c0cf4"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","hash":"682b375aaded1c742fb9b7f2048ecef3"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ar.dart","hash":"149edbe6638853ef73513e2d10808837"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","hash":"2f63b5f8cc2603056e8afae563d5ebeb"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_uk.arb","hash":"18734cbdc4ca08381dbf05edbb04e764"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_it.arb","hash":"e1330022bdc52a31cdec5ed56ec3509e"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_de.arb","hash":"d36cae88dcd2083fd934b7973a71bc6c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","hash":"640c06b0f22964611cf9b6b47939e8aa"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_he.dart","hash":"8869673f63a885119d38639078278205"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","hash":"032ae47fe9c19834e14411871a4f6137"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","hash":"8891455a6ec548c14b849b66380846a6"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ro.arb","hash":"7fbb2929c42db782e4dfe30c7d8a6375"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sv.dart","hash":"8df8d70c68d748a6a340201bdd1e3d28"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_et.dart","hash":"fc25800930f961de8615f0443e55111d"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_en.dart","hash":"2ed9eae7543bd1af982f9e44b2a9a8ac"},{"path":"/Users/amanraj/development/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","hash":"d9155a79b0ac6660fef528508997f61d"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","hash":"dc7e80ad8cea661ea613b0f5b36c9662"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sl.arb","hash":"e45f59e1bdbe6f47b870919182a6ea12"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hu.arb","hash":"ab0f09b5bd538dd9541f42baec4be64a"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_da.dart","hash":"c1d662b7aa3d509c0654aaf6745dee32"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","hash":"9e5f514aa7b96c42dd74b6df60c15deb"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ko.dart","hash":"328e781dfb8415bcee4b5593c2f062c4"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","hash":"95433370dc0f99c299e3b668e291e97b"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_et.arb","hash":"9d958d6ee4550c5346d8202428663869"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lt.dart","hash":"655745a258c77086c7c270cbe1b7f73c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ca.dart","hash":"b2b1dee28e0e6bcaf937f7674aa44bab"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_gu.dart","hash":"74520d69bf0ada9fad06fcc721a433cc"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","hash":"ca78debc0d042bd2883ae9371dd74f75"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_es.dart","hash":"9b20c833aca1505b3fb2e35cec22874d"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/l10n.yaml","hash":"7f1fe83b9f37bd55ea049a6a2e5d1abe"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ti.arb","hash":"1946b5c8fbefe5af85a74af8995a29bd"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_be.dart","hash":"78a73dadfd1cfea583ff650180498367"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_gu.arb","hash":"93ee531a43623d62697085e9a5d9ccbe"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ml.arb","hash":"92a50c05f4e3ae0f369b72479a732c9f"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","hash":"de6e7fcb10b5297abfebea313aaaf9d1"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations.dart","hash":"2786bede1a68cdbdbdb9b7fe99750d12"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","hash":"d1c0176b94b78c6d17ce12eb0085e6e6"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","hash":"8bf38e1bde975e58ccf64b9ba9d6b626"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_el.dart","hash":"788cb4e18371523c3fd7c2e7f892fa33"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lv.arb","hash":"a3c8ba63da0a61f93e23029d0badc25e"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fi.dart","hash":"3140c370adc7afcca2a69867ad9eb25f"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ml.dart","hash":"9b8007a4960c931d5889d1a6ac8ffd1c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_it.dart","hash":"f81c45df665770564270999d90347c18"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ca.arb","hash":"993f8367aff09ed68fee0048e74b0564"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_bg.dart","hash":"d5efbe5faa1ce67d32a37a2c3991213f"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fa.dart","hash":"92dce41248d0c1af2abdfd1eba8918b7"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_nl.dart","hash":"ed130f58c9c441a64edda2923a91fd74"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","hash":"a9f5860ba6b6218b0fe1254c0effed45"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","hash":"71030f741e1952b92b63200a2170f120"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_km.dart","hash":"aa68d9181e72b527dde2b629cd40260b"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fa.arb","hash":"e55f440d7bdd2c3cd692c0ea457065ff"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fi.arb","hash":"fa18867ceb7220ba33ca4cfa957bd379"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_vi.dart","hash":"11f04682cabeb10efac5c389b459511b"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lv.dart","hash":"24d83f73d369c974b5f696588fa19dbc"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sr.dart","hash":"9d5a35e934eed834f49f6d624201a9c0"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","hash":"18534f8448f44f96912e582465b59b49"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_de.dart","hash":"5592fa613b1359b7a9e934dd3fa11818"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fr.dart","hash":"51b2114781d4deea0e8581cdad739f4c"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ro.dart","hash":"eeae7228eec576809a8422db24b68e56"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_he.arb","hash":"2b18231e6b963c0debe48ba59b44f7bd"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","hash":"4a5d7a9141bf629facd0e7a8f43eabc1"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","hash":"4b3c05b1759ce7dd457c340e207cbcc3"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ti.dart","hash":"d74b061d22f07198eab740961c9faea5"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","hash":"3efca77cf8ac8d7533002055969db806"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb","hash":"5f197f457e206aee5bfcf073af2f4f32"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb","hash":"c34e7122476d72931cab5f4ca6997886"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","hash":"2ebf7d21d3c24e81374e2292b0f90fa5"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hu.dart","hash":"1bdfc1fca4ab0337df08c9927035f5fd"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ru.dart","hash":"8fd88edab7eb3a41ece8c0c07fd3d557"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","hash":"0349a95439507fd0ba360e296879dfa5"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","hash":"67a52094e4e350e731a3bd433edf2716"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","hash":"d6f114efef8606608fdaa1c188b4398d"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pt.dart","hash":"72b194e8ad05bdf5f535087a85bb40ee"},{"path":"/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_cs.dart","hash":"e588ad1f68ce5ff0a0591a307398104e"}]} \ No newline at end of file diff --git a/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_l10n_inputs_and_outputs.json b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_l10n_inputs_and_outputs.json new file mode 100644 index 0000000000..5ce7d8cba6 --- /dev/null +++ b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_l10n_inputs_and_outputs.json @@ -0,0 +1 @@ +{"inputs":["/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_be.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ca.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_de.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_et.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fa.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_gu.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_he.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hu.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_it.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ka.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_km.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lv.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ml.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ro.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ti.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_uk.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ar.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_be.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_bg.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ca.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_cs.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_da.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_de.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_el.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_en.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_es.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_et.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fa.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_gu.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_he.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hu.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_id.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_it.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ja.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ka.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_km.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ko.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lt.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lv.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ml.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_nl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pt.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ro.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ru.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sk.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sv.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ti.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_tr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_uk.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_vi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_zh.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.d b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.d new file mode 100644 index 0000000000..7d24383260 --- /dev/null +++ b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.d @@ -0,0 +1 @@ + /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ar.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_be.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_bg.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ca.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_cs.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_da.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_de.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_el.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_en.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_es.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_et.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fa.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fi.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fr.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_gu.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_he.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hi.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hu.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_id.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_it.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ja.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ka.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_km.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ko.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lt.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lv.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ml.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_nl.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pl.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pt.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ro.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ru.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sk.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sl.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sr.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sv.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ti.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_tr.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_uk.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_vi.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_zh.dart /Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations.dart: /Users/amanraj/development/ente/mobile/packages/strings/l10n.yaml /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_be.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ca.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_de.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_et.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fa.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fi.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_gu.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_he.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hi.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hu.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_it.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ka.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_km.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lv.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ml.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ro.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sl.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ti.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_uk.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb /Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb \ No newline at end of file diff --git a/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.stamp b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.stamp new file mode 100644 index 0000000000..5279ec26f9 --- /dev/null +++ b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/gen_localizations.stamp @@ -0,0 +1 @@ +{"inputs":["/Users/amanraj/development/flutter/packages/flutter_tools/lib/src/build_system/targets/localizations.dart","/Users/amanraj/development/ente/mobile/packages/strings/l10n.yaml","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ar.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_be.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_bg.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ca.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_cs.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_da.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_de.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_el.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_en.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_es.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_et.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fa.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_fr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_gu.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_he.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_hu.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_id.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_it.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ja.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ka.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_km.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ko.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lt.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_lv.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ml.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_nl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_pt.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ro.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ru.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sk.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sl.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_sv.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_ti.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_tr.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_uk.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_vi.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_CN.arb","/Users/amanraj/development/ente/mobile/packages/strings/lib/l10n/arb/strings_zh_TW.arb"],"outputs":["/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ar.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_be.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_bg.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ca.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_cs.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_da.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_de.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_el.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_en.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_es.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_et.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fa.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_fr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_gu.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_he.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_hu.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_id.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_it.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ja.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ka.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_km.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ko.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lt.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_lv.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ml.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_nl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_pt.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ro.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ru.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sk.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sl.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_sv.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_ti.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_tr.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_uk.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_vi.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations_zh.dart","/Users/amanraj/development/ente/mobile/packages/strings/.dart_tool/flutter_gen/gen_l10n/strings_localizations.dart"]} \ No newline at end of file diff --git a/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/outputs.json b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/outputs.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/mobile/packages/strings/build/3d559b893a35c16b02d8e7441d0112a4/outputs.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/mobile/packages/strings/pubspec.lock b/mobile/packages/strings/pubspec.lock index 97843e33f0..3ec615e8a2 100644 --- a/mobile/packages/strings/pubspec.lock +++ b/mobile/packages/strings/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -76,26 +76,26 @@ packages: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -108,18 +108,18 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" matcher: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -132,71 +132,71 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" vector_math: dependency: transitive description: @@ -209,10 +209,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" sdks: - dart: ">=3.7.0-0 <4.0.0" + dart: ">=3.5.0 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/mobile/packages/ui/lib/components/menu_item_widget.dart b/mobile/packages/ui/lib/components/menu_item_widget.dart index 1c1d97ec8e..892b846493 100644 --- a/mobile/packages/ui/lib/components/menu_item_widget.dart +++ b/mobile/packages/ui/lib/components/menu_item_widget.dart @@ -3,6 +3,7 @@ import 'package:ente_ui/components/menu_item_child_widgets.dart'; import 'package:ente_ui/models/execution_states.dart'; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:ente_utils/debouncer.dart'; +import "package:expandable/expandable.dart"; import 'package:flutter/material.dart'; class MenuItemWidget extends StatefulWidget { @@ -40,7 +41,7 @@ class MenuItemWidget extends StatefulWidget { final double singleBorderRadius; final double multipleBorderRadius; final Color? pressedColor; - final ExpansibleController? expandableController; + final ExpandableController? expandableController; final bool isBottomBorderRadiusRemoved; final bool isTopBorderRadiusRemoved; @@ -152,7 +153,7 @@ class _MenuItemWidgetState extends State { Widget menuItemWidget(BuildContext context) { final circularRadius = Radius.circular(borderRadius); - final isExpanded = widget.expandableController?.isExpanded; + final isExpanded = widget.expandableController?.value; final bottomBorderRadius = (isExpanded != null && isExpanded) || widget.isBottomBorderRadiusRemoved ? const Radius.circular(0) diff --git a/mobile/packages/ui/lib/theme/ente_theme_data.dart b/mobile/packages/ui/lib/theme/ente_theme_data.dart index 8c2cf18786..c2a4b3790e 100644 --- a/mobile/packages/ui/lib/theme/ente_theme_data.dart +++ b/mobile/packages/ui/lib/theme/ente_theme_data.dart @@ -39,7 +39,7 @@ final lightThemeData = ThemeData( bodyLarge: const TextStyle(color: Colors.orange), ), cardColor: const Color.fromRGBO(250, 250, 250, 1.0), - dialogTheme: const DialogThemeData().copyWith( + dialogTheme: const DialogTheme().copyWith( backgroundColor: const Color.fromRGBO(250, 250, 250, 1.0), // titleTextStyle: const TextStyle( color: Colors.black, @@ -143,7 +143,7 @@ final darkThemeData = ThemeData( elevation: 0, ), cardColor: const Color.fromRGBO(10, 15, 15, 1.0), - dialogTheme: const DialogThemeData().copyWith( + dialogTheme: const DialogTheme().copyWith( backgroundColor: const Color.fromRGBO(15, 15, 15, 1.0), titleTextStyle: const TextStyle( color: Colors.white, diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index a07b26b3e9..49dd75fa9e 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: transitive description: @@ -37,34 +37,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" dio: dependency: "direct main" description: @@ -196,22 +196,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -224,10 +232,10 @@ packages: dependency: transitive description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.2.14" fixnum: dependency: transitive description: @@ -245,10 +253,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "6.0.3" flutter_inappwebview: dependency: "direct main" description: @@ -435,18 +443,18 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" intl: dependency: transitive description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" js: dependency: transitive description: @@ -467,18 +475,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -491,10 +499,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: transitive description: @@ -507,10 +515,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -523,10 +531,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: transitive description: @@ -563,10 +571,10 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: transitive description: @@ -579,10 +587,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -731,10 +739,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -779,7 +787,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -800,10 +808,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -816,50 +824,50 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: transitive description: @@ -880,18 +888,18 @@ packages: dependency: transitive description: name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.14" url_launcher_ios: dependency: transitive description: @@ -928,10 +936,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -960,10 +968,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -976,10 +984,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -992,10 +1000,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.3" xdg_directories: dependency: transitive description: @@ -1005,5 +1013,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml index 8cc504a4b0..ca07fe12ea 100644 --- a/mobile/packages/ui/pubspec.yaml +++ b/mobile/packages/ui/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: path: ../../packages/strings ente_utils: path: ../../packages/utils + expandable: ^5.0.1 flutter: sdk: flutter flutter_inappwebview: @@ -29,7 +30,7 @@ dependencies: fluttertoast: ^8.1.1 modal_bottom_sheet: ^3.0.0 shared_preferences: ^2.5.3 - window_manager: ^0.5.0 + window_manager: ^0.4.2 dev_dependencies: flutter_lints: ^5.0.0 diff --git a/mobile/packages/utils/lib/platform_util.dart b/mobile/packages/utils/lib/platform_util.dart index 6ad91b0d88..e84349c301 100644 --- a/mobile/packages/utils/lib/platform_util.dart +++ b/mobile/packages/utils/lib/platform_util.dart @@ -43,14 +43,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - fileExtension: extension, + ext: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - fileExtension: extension, + ext: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index f846d030da..a63e28947e 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.13.0" + version: "2.11.0" bip39: dependency: transitive description: @@ -37,34 +37,34 @@ packages: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" convert: dependency: transitive description: @@ -109,10 +109,10 @@ packages: dependency: transitive description: name: device_info_plus_platform_interface - sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.2" dio: dependency: transitive description: @@ -196,22 +196,30 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + expandable: + dependency: transitive + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.3" + version: "1.3.1" ffi: dependency: transitive description: name: ffi - sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.3" file: dependency: transitive description: @@ -224,10 +232,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "017a127de686af2d2fbbd64afea97052d95f2a0f87d19d25b87e097407bf9c1e" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.2.14" fixnum: dependency: transitive description: @@ -245,10 +253,10 @@ packages: dependency: "direct main" description: name: flutter_email_sender - sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "6.0.3" flutter_inappwebview: dependency: transitive description: @@ -435,18 +443,18 @@ packages: dependency: transitive description: name: http_parser - sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "4.1.2" + version: "4.0.2" intl: dependency: "direct main" description: name: intl - sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.20.2" + version: "0.19.0" js: dependency: transitive description: @@ -467,18 +475,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.9" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.9" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -491,10 +499,10 @@ packages: dependency: transitive description: name: lints - sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "5.1.1" + version: "5.0.0" logging: dependency: "direct main" description: @@ -507,10 +515,10 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: @@ -523,10 +531,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.15.0" mime: dependency: transitive description: @@ -563,10 +571,10 @@ packages: dependency: "direct main" description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.9.0" path_provider: dependency: "direct main" description: @@ -579,10 +587,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.17" + version: "2.2.15" path_provider_foundation: dependency: transitive description: @@ -731,10 +739,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" url: "https://pub.dev" source: hosted - version: "2.4.10" + version: "2.4.7" shared_preferences_foundation: dependency: transitive description: @@ -779,7 +787,7 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" sodium: dependency: transitive description: @@ -800,10 +808,10 @@ packages: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" sprintf: dependency: transitive description: @@ -816,50 +824,50 @@ packages: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" synchronized: dependency: transitive description: name: synchronized - sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.4.0" + version: "3.3.0+3" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.7.2" tuple: dependency: transitive description: @@ -880,18 +888,18 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8 + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.dev" source: hosted - version: "6.3.2" + version: "6.3.1" url_launcher_android: dependency: transitive description: name: url_launcher_android - sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" url: "https://pub.dev" source: hosted - version: "6.3.16" + version: "6.3.14" url_launcher_ios: dependency: transitive description: @@ -928,10 +936,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" url: "https://pub.dev" source: hosted - version: "2.4.1" + version: "2.3.3" url_launcher_windows: dependency: transitive description: @@ -960,10 +968,10 @@ packages: dependency: transitive description: name: vm_service - sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted - version: "15.0.0" + version: "14.2.5" web: dependency: transitive description: @@ -976,10 +984,10 @@ packages: dependency: transitive description: name: win32 - sha256: "66814138c3562338d05613a6e368ed8cfb237ad6d64a9e9334be3f309acfca03" + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e url: "https://pub.dev" source: hosted - version: "5.14.0" + version: "5.10.1" win32_registry: dependency: transitive description: @@ -992,10 +1000,10 @@ packages: dependency: "direct main" description: name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.3" xdg_directories: dependency: transitive description: @@ -1005,5 +1013,5 @@ packages: source: hosted version: "1.1.0" sdks: - dart: ">=3.8.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 4ec3dd3c90..6a6665863d 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -18,18 +18,18 @@ dependencies: path: ../../packages/strings ente_ui: path: ../../packages/ui - file_saver: ^0.3.0 + file_saver: ^0.2.14 flutter: sdk: flutter - flutter_email_sender: ^7.0.0 - intl: ^0.20.1 + flutter_email_sender: ^6.0.2 + intl: ^0.19.0 logging: ^1.3.0 package_info_plus: ^8.1.1 - path: ^1.9.1 + path: ^1.9.0 path_provider: ^2.1.5 share_plus: ^11.0.0 url_launcher: ^6.3.1 - window_manager: ^0.5.0 + window_manager: ^0.4.2 dev_dependencies: From cb55be1e5c1c4741d04a1fe7fd543022d939a5b9 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Mon, 4 Aug 2025 19:56:45 +0530 Subject: [PATCH 117/164] Fix android build From 7adb1c0a6c24621df946c6102cd0778067cea352 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 12:07:21 +0530 Subject: [PATCH 118/164] Minor fix --- mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart index ecdb19f29b..fc80616f9f 100644 --- a/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart +++ b/mobile/apps/auth/lib/ui/components/toggle_switch_widget.dart @@ -1,6 +1,5 @@ import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/models/execution_states.dart'; -import 'package:ente_auth/theme/colors.dart'; +import 'package:ente_auth/models/execution_states.dart'; import 'package:ente_auth/ui/common/loading_widget.dart'; import 'package:ente_auth/utils/debouncer.dart'; import 'package:ente_base/typedefs.dart'; From 0a3035e5d54ba6470000dea53b5cfda83b835177 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 14:33:19 +0530 Subject: [PATCH 119/164] Update Android build configuration: enable core library desugaring, increase minSdkVersion to 22, and upgrade Gradle version to 8.4. Add ProGuard rules to suppress warnings. --- mobile/apps/auth/android/app/build.gradle | 7 +++++-- mobile/apps/auth/android/app/proguard-rules.pro | 6 ++++++ mobile/apps/auth/android/build.gradle | 13 +++++++++++++ .../gradle/wrapper/gradle-wrapper.properties | 2 +- mobile/apps/auth/android/settings.gradle | 2 +- 5 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 mobile/apps/auth/android/app/proguard-rules.pro diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index d8ffec49e8..930690df6c 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -34,6 +34,7 @@ android { ndkVersion flutter.ndkVersion compileOptions { + coreLibraryDesugaringEnabled = true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -56,7 +57,7 @@ android { applicationId "io.ente.auth" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion 21 + minSdkVersion 22 targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName @@ -115,4 +116,6 @@ flutter { source '../..' } -dependencies {} +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} diff --git a/mobile/apps/auth/android/app/proguard-rules.pro b/mobile/apps/auth/android/app/proguard-rules.pro new file mode 100644 index 0000000000..bfd553ce58 --- /dev/null +++ b/mobile/apps/auth/android/app/proguard-rules.pro @@ -0,0 +1,6 @@ +# Please add these rules to your existing keep rules in order to suppress warnings. +# This is generated automatically by the Android Gradle plugin. +-dontwarn com.google.errorprone.annotations.CanIgnoreReturnValue +-dontwarn com.google.errorprone.annotations.CheckReturnValue +-dontwarn com.google.errorprone.annotations.Immutable +-dontwarn com.google.errorprone.annotations.RestrictedApi \ No newline at end of file diff --git a/mobile/apps/auth/android/build.gradle b/mobile/apps/auth/android/build.gradle index bc157bd1a1..b3c47285d6 100644 --- a/mobile/apps/auth/android/build.gradle +++ b/mobile/apps/auth/android/build.gradle @@ -6,6 +6,19 @@ allprojects { } rootProject.buildDir = '../build' + +subprojects { + afterEvaluate { project -> + if (project.hasProperty('android')) { + project.android { + if (namespace == null) { + namespace project.group + } + } + } + } +} + subprojects { project.buildDir = "${rootProject.buildDir}/${project.name}" } diff --git a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties index e1ca574ef0..9c5194d3a2 100644 --- a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip \ No newline at end of file diff --git a/mobile/apps/auth/android/settings.gradle b/mobile/apps/auth/android/settings.gradle index 748caceba7..58725889bd 100644 --- a/mobile/apps/auth/android/settings.gradle +++ b/mobile/apps/auth/android/settings.gradle @@ -19,7 +19,7 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false + id "com.android.application" version "8.2.0" apply false id "org.jetbrains.kotlin.android" version "1.8.22" apply false } From 0e61b3dfd4adcbe1547791cc8447bae59f97a5d6 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 14:35:03 +0530 Subject: [PATCH 120/164] Minor changes in packages's pubspec to match dependencies --- mobile/apps/auth/pubspec.lock | 151 +++++++++++++---------- mobile/apps/auth/pubspec.yaml | 6 +- mobile/packages/accounts/pubspec.lock | 24 ++-- mobile/packages/accounts/pubspec.yaml | 2 +- mobile/packages/lock_screen/pubspec.lock | 24 ++-- mobile/packages/ui/pubspec.lock | 97 ++++++--------- mobile/packages/ui/pubspec.yaml | 6 +- mobile/packages/utils/pubspec.lock | 20 +-- mobile/packages/utils/pubspec.yaml | 4 +- 9 files changed, 156 insertions(+), 178 deletions(-) diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 7ab765cd10..57852f892d 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -82,10 +82,10 @@ packages: dependency: transitive description: name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.dev" source: hosted - version: "2.5.0" + version: "2.7.0" async: dependency: transitive description: @@ -613,21 +613,19 @@ packages: flutter_inappwebview: dependency: "direct main" description: - path: flutter_inappwebview - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "6.2.0-beta.3" + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" flutter_inappwebview_android: dependency: transitive description: - path: flutter_inappwebview_android - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -639,48 +637,43 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - path: flutter_inappwebview_ios - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_macos: dependency: transitive description: - path: flutter_inappwebview_macos - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_platform_interface: dependency: transitive description: - path: flutter_inappwebview_platform_interface - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.4.0-beta.3" + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" flutter_inappwebview_web: dependency: transitive description: - path: flutter_inappwebview_web - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_windows: dependency: transitive description: - path: flutter_inappwebview_windows - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "0.7.0-beta.3" + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" flutter_launcher_icons: dependency: "direct main" description: @@ -933,10 +926,10 @@ packages: dependency: "direct main" description: name: http - sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.4.0" http_multi_server: dependency: transitive description: @@ -965,10 +958,10 @@ packages: dependency: transitive description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: f31d52537dc417fdcde36088fdf11d191026fd5e4fae742491ebd40e5a8bea7d url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.3.0" intl: dependency: "direct main" description: @@ -1221,18 +1214,18 @@ packages: dependency: "direct main" description: name: package_info_plus - sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918 + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" url: "https://pub.dev" source: hosted - version: "8.0.2" + version: "8.3.0" package_info_plus_platform_interface: dependency: transitive description: name: package_info_plus_platform_interface - sha256: ac1f4a4847f1ade8e6a87d1f39f5d7c67490738642e2542f559ec38c37489a66 + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.2.0" password_strength: dependency: "direct main" description: @@ -1269,10 +1262,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.5" path_provider_android: dependency: transitive description: @@ -1325,10 +1318,10 @@ packages: dependency: "direct main" description: name: pinput - sha256: "7bf9aa7d0eeb3da9f7d49d2087c7bc7d36cd277d2e94cc31c6da52e1ebb048d0" + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.0.1" platform: dependency: transitive description: @@ -1365,10 +1358,10 @@ packages: dependency: "direct main" description: name: privacy_screen - sha256: b80297d2726d96e8a8341149e81a415302755f02d3af7c05c820d9e191bbfbee + sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" url: "https://pub.dev" source: hosted - version: "0.0.6" + version: "0.0.8" protobuf: dependency: "direct main" description: @@ -1429,10 +1422,42 @@ packages: dependency: transitive description: name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" url: "https://pub.dev" source: hosted - version: "0.1.9" + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" sentry: dependency: "direct main" description: @@ -1469,10 +1494,10 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.5.3" shared_preferences_android: dependency: transitive description: @@ -1778,7 +1803,7 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted version: "1.4.0" diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 1ff8e357f0..ee8d176587 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -63,11 +63,7 @@ dependencies: flutter_email_sender: ^6.0.2 # revert to pub.dev when merged # https://github.com/pichillilorenzo/flutter_inappwebview/pull/2548 - flutter_inappwebview: - git: - url: https://github.com/pichillilorenzo/flutter_inappwebview.git - path: flutter_inappwebview - ref: 3e6c4c4a25340cd363af9d38891d88498b90be26 + flutter_inappwebview: ^6.1.4 flutter_launcher_icons: ^0.14.1 flutter_local_authentication: git: diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 9e7b4bcd4f..65c6eabdf6 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "3.6.1" args: dependency: transitive description: @@ -877,14 +877,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" privacy_screen: dependency: transitive description: @@ -961,18 +953,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "10.1.4" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.0.2" shared_preferences: dependency: "direct main" description: @@ -1286,10 +1278,10 @@ packages: dependency: transitive description: name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.3" xdg_directories: dependency: transitive description: diff --git a/mobile/packages/accounts/pubspec.yaml b/mobile/packages/accounts/pubspec.yaml index e787f25add..3faa1aed42 100644 --- a/mobile/packages/accounts/pubspec.yaml +++ b/mobile/packages/accounts/pubspec.yaml @@ -39,7 +39,7 @@ dependencies: password_strength: ^0.2.0 pinput: ^5.0.1 pointycastle: ^3.7.3 - share_plus: ^11.0.0 + share_plus: ^10.0.2 shared_preferences: ^2.2.2 step_progress_indicator: ^1.0.2 styled_text: ^8.1.0 diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index ec4bcab64c..8f1d956a6c 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "3.6.1" args: dependency: transitive description: @@ -877,14 +877,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" privacy_screen: dependency: "direct main" description: @@ -961,18 +953,18 @@ packages: dependency: transitive description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "10.1.4" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.0.2" shared_preferences: dependency: "direct main" description: @@ -1286,10 +1278,10 @@ packages: dependency: transitive description: name: window_manager - sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.4.3" xdg_directories: dependency: transitive description: diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index 49dd75fa9e..1cce45940c 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "3.6.1" args: dependency: transitive description: @@ -260,21 +260,19 @@ packages: flutter_inappwebview: dependency: "direct main" description: - path: flutter_inappwebview - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "6.2.0-beta.3" + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" flutter_inappwebview_android: dependency: transitive description: - path: flutter_inappwebview_android - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" flutter_inappwebview_internal_annotations: dependency: transitive description: @@ -286,48 +284,43 @@ packages: flutter_inappwebview_ios: dependency: transitive description: - path: flutter_inappwebview_ios - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_macos: dependency: transitive description: - path: flutter_inappwebview_macos - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_platform_interface: dependency: transitive description: - path: flutter_inappwebview_platform_interface - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.4.0-beta.3" + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" flutter_inappwebview_web: dependency: transitive description: - path: flutter_inappwebview_web - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "1.2.0-beta.3" + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" flutter_inappwebview_windows: dependency: transitive description: - path: flutter_inappwebview_windows - ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - resolved-ref: "3e6c4c4a25340cd363af9d38891d88498b90be26" - url: "https://github.com/pichillilorenzo/flutter_inappwebview.git" - source: git - version: "0.7.0-beta.3" + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" flutter_lints: dependency: "direct dev" description: @@ -647,14 +640,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" screen_retriever: dependency: transitive description: @@ -715,18 +700,18 @@ packages: dependency: transitive description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "10.1.4" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.0.2" shared_preferences: dependency: "direct main" description: diff --git a/mobile/packages/ui/pubspec.yaml b/mobile/packages/ui/pubspec.yaml index ca07fe12ea..1c28753b77 100644 --- a/mobile/packages/ui/pubspec.yaml +++ b/mobile/packages/ui/pubspec.yaml @@ -22,11 +22,7 @@ dependencies: expandable: ^5.0.1 flutter: sdk: flutter - flutter_inappwebview: - git: - url: https://github.com/pichillilorenzo/flutter_inappwebview.git - path: flutter_inappwebview - ref: 3e6c4c4a25340cd363af9d38891d88498b90be26 + flutter_inappwebview: ^6.1.4 fluttertoast: ^8.1.1 modal_bottom_sheet: ^3.0.0 shared_preferences: ^2.5.3 diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index a63e28947e..8bcb5b1b12 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: "direct main" description: name: archive - sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d url: "https://pub.dev" source: hosted - version: "4.0.7" + version: "3.6.1" args: dependency: transitive description: @@ -647,14 +647,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" - posix: - dependency: transitive - description: - name: posix - sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" - source: hosted - version: "6.0.3" screen_retriever: dependency: transitive description: @@ -715,18 +707,18 @@ packages: dependency: "direct main" description: name: share_plus - sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da url: "https://pub.dev" source: hosted - version: "11.0.0" + version: "10.1.4" share_plus_platform_interface: dependency: transitive description: name: share_plus_platform_interface - sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "5.0.2" shared_preferences: dependency: transitive description: diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 6a6665863d..11ce5fda17 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -8,7 +8,7 @@ environment: flutter: ">=1.17.0" dependencies: - archive: ^4.0.7 + archive: ^3.3.7 email_validator: ^3.0.0 ente_configuration: path: ../../packages/configuration @@ -27,7 +27,7 @@ dependencies: package_info_plus: ^8.1.1 path: ^1.9.0 path_provider: ^2.1.5 - share_plus: ^11.0.0 + share_plus: ^10.0.2 url_launcher: ^6.3.1 window_manager: ^0.4.2 From cc2d65d796ae489a51c0e1a5b393948768984ffe Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 14:35:25 +0530 Subject: [PATCH 121/164] Fix string --- .../strings/lib/l10n/arb/strings_en.arb | 239 +++++ .../lib/l10n/strings_localizations.dart | 348 +++++++ .../lib/l10n/strings_localizations_ar.dart | 396 ++++++-- .../lib/l10n/strings_localizations_bg.dart | 398 ++++++-- .../lib/l10n/strings_localizations_cs.dart | 392 +++++-- .../lib/l10n/strings_localizations_da.dart | 388 +++++-- .../lib/l10n/strings_localizations_el.dart | 400 ++++++-- .../lib/l10n/strings_localizations_en.dart | 191 ++++ .../lib/l10n/strings_localizations_es.dart | 400 ++++++-- .../lib/l10n/strings_localizations_fr.dart | 405 ++++++-- .../lib/l10n/strings_localizations_id.dart | 397 ++++++-- .../lib/l10n/strings_localizations_ja.dart | 331 +----- .../lib/l10n/strings_localizations_ko.dart | 330 ------ .../lib/l10n/strings_localizations_lt.dart | 395 ++++++-- .../lib/l10n/strings_localizations_nl.dart | 399 ++++++-- .../lib/l10n/strings_localizations_pl.dart | 395 ++++++-- .../lib/l10n/strings_localizations_pt.dart | 396 ++++++-- .../lib/l10n/strings_localizations_ru.dart | 403 ++++++-- .../lib/l10n/strings_localizations_sk.dart | 392 +++++-- .../lib/l10n/strings_localizations_sr.dart | 397 ++++++-- .../lib/l10n/strings_localizations_sv.dart | 395 ++++++-- .../lib/l10n/strings_localizations_tr.dart | 399 ++++++-- .../lib/l10n/strings_localizations_vi.dart | 393 ++++++-- .../lib/l10n/strings_localizations_zh.dart | 954 +++++++++++++++--- 24 files changed, 6934 insertions(+), 2599 deletions(-) diff --git a/mobile/packages/strings/lib/l10n/arb/strings_en.arb b/mobile/packages/strings/lib/l10n/arb/strings_en.arb index c9eb17a1ab..6db1039109 100644 --- a/mobile/packages/strings/lib/l10n/arb/strings_en.arb +++ b/mobile/packages/strings/lib/l10n/arb/strings_en.arb @@ -540,5 +540,244 @@ "recover": "Recover", "@recover": { "description": "Recover button label" + }, + "loggingOut": "Logging out...", + "@loggingOut": { + "description": "Message shown while logging out" + }, + "immediately": "Immediately", + "@immediately": { + "description": "Immediately option for auto lock timing" + }, + "appLock": "App lock", + "@appLock": { + "description": "App lock setting title" + }, + "autoLock": "Auto lock", + "@autoLock": { + "description": "Auto lock setting title" + }, + "noSystemLockFound": "No system lock found", + "@noSystemLockFound": { + "description": "Error when no system lock is found" + }, + "deviceLockEnablePreSteps": "To enable device lock, please setup device passcode or screen lock in your system settings.", + "@deviceLockEnablePreSteps": { + "description": "Instructions for enabling device lock" + }, + "appLockDescription": "Choose between your device's default lock screen and a custom lock screen with a PIN or password.", + "@appLockDescription": { + "description": "Description of app lock feature" + }, + "deviceLock": "Device lock", + "@deviceLock": { + "description": "Device lock option title" + }, + "pinLock": "Pin lock", + "@pinLock": { + "description": "PIN lock option title" + }, + "autoLockFeatureDescription": "Time after which the app locks after being put in the background", + "@autoLockFeatureDescription": { + "description": "Description of auto lock feature" + }, + "hideContent": "Hide content", + "@hideContent": { + "description": "Hide content setting title" + }, + "hideContentDescriptionAndroid": "Hides app content in the app switcher and disables screenshots", + "@hideContentDescriptionAndroid": { + "description": "Description of hide content feature on Android" + }, + "hideContentDescriptioniOS": "Hides app content in the app switcher", + "@hideContentDescriptioniOS": { + "description": "Description of hide content feature on iOS" + }, + "tooManyIncorrectAttempts": "Too many incorrect attempts", + "@tooManyIncorrectAttempts": { + "description": "Message shown when too many incorrect attempts are made" + }, + "tapToUnlock": "Tap to unlock", + "@tapToUnlock": { + "description": "Message prompting user to tap to unlock" + }, + "areYouSureYouWantToLogout": "Are you sure you want to logout?", + "@areYouSureYouWantToLogout": { + "description": "Confirmation message before logout" + }, + "yesLogout": "Yes, logout", + "@yesLogout": { + "description": "Confirmation button for logout" + }, + "authToViewSecrets": "Please authenticate to view your secrets", + "@authToViewSecrets": { + "description": "Message prompting authentication to view secrets" + }, + "next": "Next", + "@next": { + "description": "Next button label" + }, + "setNewPassword": "Set new password", + "@setNewPassword": { + "description": "Set new password title" + }, + "enterPin": "Enter PIN", + "@enterPin": { + "description": "Enter PIN prompt" + }, + "setNewPin": "Set new PIN", + "@setNewPin": { + "description": "Set new PIN title" + }, + "confirm": "Confirm", + "@confirm": { + "description": "Confirm button label" + }, + "reEnterPassword": "Re-enter password", + "@reEnterPassword": { + "description": "Re-enter password prompt" + }, + "reEnterPin": "Re-enter PIN", + "@reEnterPin": { + "description": "Re-enter PIN prompt" + }, + "androidBiometricHint": "Verify identity", + "@androidBiometricHint": { + "description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricNotRecognized": "Not recognized. Try again.", + "@androidBiometricNotRecognized": { + "description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricSuccess": "Success", + "@androidBiometricSuccess": { + "description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters." + }, + "androidCancelButton": "Cancel", + "@androidCancelButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters." + }, + "androidSignInTitle": "Authentication required", + "@androidSignInTitle": { + "description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters." + }, + "androidBiometricRequiredTitle": "Biometric required", + "@androidBiometricRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsRequiredTitle": "Device credentials required", + "@androidDeviceCredentialsRequiredTitle": { + "description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters." + }, + "androidDeviceCredentialsSetupDescription": "Device credentials required", + "@androidDeviceCredentialsSetupDescription": { + "description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side." + }, + "goToSettings": "Go to settings", + "@goToSettings": { + "description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters." + }, + "androidGoToSettingsDescription": "Biometric authentication is not set up on your device. Go to 'Settings > Security' to add biometric authentication.", + "@androidGoToSettingsDescription": { + "description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side." + }, + "iOSLockOut": "Biometric authentication is disabled. Please lock and unlock your screen to enable it.", + "@iOSLockOut": { + "description": "Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side." + }, + "iOSOkButton": "OK", + "@iOSOkButton": { + "description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters." + }, + "emailAlreadyRegistered": "Email already registered.", + "@emailAlreadyRegistered": { + "description": "Error message when email is already registered" + }, + "emailNotRegistered": "Email not registered.", + "@emailNotRegistered": { + "description": "Error message when email is not registered" + }, + "thisEmailIsAlreadyInUse": "This email is already in use", + "@thisEmailIsAlreadyInUse": { + "description": "Error message when email is already in use" + }, + "emailChangedTo": "Email changed to {newEmail}", + "@emailChangedTo": { + "description": "Message when email has been changed", + "placeholders": { + "newEmail": { + "description": "The new email address", + "type": "String", + "example": "new@example.com" + } + } + }, + "authenticationFailedPleaseTryAgain": "Authentication failed, please try again", + "@authenticationFailedPleaseTryAgain": { + "description": "Error message when authentication fails" + }, + "authenticationSuccessful": "Authentication successful!", + "@authenticationSuccessful": { + "description": "Success message when authentication is successful" + }, + "sessionExpired": "Session expired", + "@sessionExpired": { + "description": "Error message when session has expired" + }, + "incorrectRecoveryKey": "Incorrect recovery key", + "@incorrectRecoveryKey": { + "description": "Error message when recovery key is incorrect" + }, + "theRecoveryKeyYouEnteredIsIncorrect": "The recovery key you entered is incorrect", + "@theRecoveryKeyYouEnteredIsIncorrect": { + "description": "Detailed error message when recovery key is incorrect" + }, + "twofactorAuthenticationSuccessfullyReset": "Two-factor authentication successfully reset", + "@twofactorAuthenticationSuccessfullyReset": { + "description": "Message when two-factor authentication is successfully reset" + }, + "noRecoveryKey": "No recovery key", + "@noRecoveryKey": { + "description": "Error message when no recovery key is found" + }, + "yourAccountHasBeenDeleted": "Your account has been deleted", + "@yourAccountHasBeenDeleted": { + "description": "Confirmation message when account has been deleted" + }, + "verificationId": "Verification ID", + "@verificationId": { + "description": "Label for verification ID" + }, + "yourVerificationCodeHasExpired": "Your verification code has expired", + "@yourVerificationCodeHasExpired": { + "description": "Error message when verification code has expired" + }, + "incorrectCode": "Incorrect code", + "@incorrectCode": { + "description": "Error message when code is incorrect" + }, + "sorryTheCodeYouveEnteredIsIncorrect": "Sorry, the code you've entered is incorrect", + "@sorryTheCodeYouveEnteredIsIncorrect": { + "description": "Detailed error message when code is incorrect" + }, + "developerSettings": "Developer settings", + "@developerSettings": { + "description": "Label for developer settings" + }, + "serverEndpoint": "Server endpoint", + "@serverEndpoint": { + "description": "Label for server endpoint setting" + }, + "invalidEndpoint": "Invalid endpoint", + "@invalidEndpoint": { + "description": "Error message when endpoint is invalid" + }, + "invalidEndpointMessage": "Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.", + "@invalidEndpointMessage": { + "description": "Detailed error message when endpoint is invalid" + }, + "endpointUpdatedMessage": "Endpoint updated successfully", + "@endpointUpdatedMessage": { + "description": "Success message when endpoint is updated" } } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations.dart b/mobile/packages/strings/lib/l10n/strings_localizations.dart index 3fc9ae3d62..c0ec261268 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations.dart @@ -946,6 +946,354 @@ abstract class StringsLocalizations { /// In en, this message translates to: /// **'Recover'** String get recover; + + /// Message shown while logging out + /// + /// In en, this message translates to: + /// **'Logging out...'** + String get loggingOut; + + /// Immediately option for auto lock timing + /// + /// In en, this message translates to: + /// **'Immediately'** + String get immediately; + + /// App lock setting title + /// + /// In en, this message translates to: + /// **'App lock'** + String get appLock; + + /// Auto lock setting title + /// + /// In en, this message translates to: + /// **'Auto lock'** + String get autoLock; + + /// Error when no system lock is found + /// + /// In en, this message translates to: + /// **'No system lock found'** + String get noSystemLockFound; + + /// Instructions for enabling device lock + /// + /// In en, this message translates to: + /// **'To enable device lock, please setup device passcode or screen lock in your system settings.'** + String get deviceLockEnablePreSteps; + + /// Description of app lock feature + /// + /// In en, this message translates to: + /// **'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'** + String get appLockDescription; + + /// Device lock option title + /// + /// In en, this message translates to: + /// **'Device lock'** + String get deviceLock; + + /// PIN lock option title + /// + /// In en, this message translates to: + /// **'Pin lock'** + String get pinLock; + + /// Description of auto lock feature + /// + /// In en, this message translates to: + /// **'Time after which the app locks after being put in the background'** + String get autoLockFeatureDescription; + + /// Hide content setting title + /// + /// In en, this message translates to: + /// **'Hide content'** + String get hideContent; + + /// Description of hide content feature on Android + /// + /// In en, this message translates to: + /// **'Hides app content in the app switcher and disables screenshots'** + String get hideContentDescriptionAndroid; + + /// Description of hide content feature on iOS + /// + /// In en, this message translates to: + /// **'Hides app content in the app switcher'** + String get hideContentDescriptioniOS; + + /// Message shown when too many incorrect attempts are made + /// + /// In en, this message translates to: + /// **'Too many incorrect attempts'** + String get tooManyIncorrectAttempts; + + /// Message prompting user to tap to unlock + /// + /// In en, this message translates to: + /// **'Tap to unlock'** + String get tapToUnlock; + + /// Confirmation message before logout + /// + /// In en, this message translates to: + /// **'Are you sure you want to logout?'** + String get areYouSureYouWantToLogout; + + /// Confirmation button for logout + /// + /// In en, this message translates to: + /// **'Yes, logout'** + String get yesLogout; + + /// Message prompting authentication to view secrets + /// + /// In en, this message translates to: + /// **'Please authenticate to view your secrets'** + String get authToViewSecrets; + + /// Next button label + /// + /// In en, this message translates to: + /// **'Next'** + String get next; + + /// Set new password title + /// + /// In en, this message translates to: + /// **'Set new password'** + String get setNewPassword; + + /// Enter PIN prompt + /// + /// In en, this message translates to: + /// **'Enter PIN'** + String get enterPin; + + /// Set new PIN title + /// + /// In en, this message translates to: + /// **'Set new PIN'** + String get setNewPin; + + /// Confirm button label + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// Re-enter password prompt + /// + /// In en, this message translates to: + /// **'Re-enter password'** + String get reEnterPassword; + + /// Re-enter PIN prompt + /// + /// In en, this message translates to: + /// **'Re-enter PIN'** + String get reEnterPin; + + /// Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Verify identity'** + String get androidBiometricHint; + + /// Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Not recognized. Try again.'** + String get androidBiometricNotRecognized; + + /// Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Success'** + String get androidBiometricSuccess; + + /// Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get androidCancelButton; + + /// Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Authentication required'** + String get androidSignInTitle; + + /// Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Biometric required'** + String get androidBiometricRequiredTitle; + + /// Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters. + /// + /// In en, this message translates to: + /// **'Device credentials required'** + String get androidDeviceCredentialsRequiredTitle; + + /// Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side. + /// + /// In en, this message translates to: + /// **'Device credentials required'** + String get androidDeviceCredentialsSetupDescription; + + /// Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'Go to settings'** + String get goToSettings; + + /// Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side. + /// + /// In en, this message translates to: + /// **'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'** + String get androidGoToSettingsDescription; + + /// Message advising the user to re-enable biometrics on their device. It shows in a dialog on iOS side. + /// + /// In en, this message translates to: + /// **'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'** + String get iOSLockOut; + + /// Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters. + /// + /// In en, this message translates to: + /// **'OK'** + String get iOSOkButton; + + /// Error message when email is already registered + /// + /// In en, this message translates to: + /// **'Email already registered.'** + String get emailAlreadyRegistered; + + /// Error message when email is not registered + /// + /// In en, this message translates to: + /// **'Email not registered.'** + String get emailNotRegistered; + + /// Error message when email is already in use + /// + /// In en, this message translates to: + /// **'This email is already in use'** + String get thisEmailIsAlreadyInUse; + + /// Message when email has been changed + /// + /// In en, this message translates to: + /// **'Email changed to {newEmail}'** + String emailChangedTo(String newEmail); + + /// Error message when authentication fails + /// + /// In en, this message translates to: + /// **'Authentication failed, please try again'** + String get authenticationFailedPleaseTryAgain; + + /// Success message when authentication is successful + /// + /// In en, this message translates to: + /// **'Authentication successful!'** + String get authenticationSuccessful; + + /// Error message when session has expired + /// + /// In en, this message translates to: + /// **'Session expired'** + String get sessionExpired; + + /// Error message when recovery key is incorrect + /// + /// In en, this message translates to: + /// **'Incorrect recovery key'** + String get incorrectRecoveryKey; + + /// Detailed error message when recovery key is incorrect + /// + /// In en, this message translates to: + /// **'The recovery key you entered is incorrect'** + String get theRecoveryKeyYouEnteredIsIncorrect; + + /// Message when two-factor authentication is successfully reset + /// + /// In en, this message translates to: + /// **'Two-factor authentication successfully reset'** + String get twofactorAuthenticationSuccessfullyReset; + + /// Error message when no recovery key is found + /// + /// In en, this message translates to: + /// **'No recovery key'** + String get noRecoveryKey; + + /// Confirmation message when account has been deleted + /// + /// In en, this message translates to: + /// **'Your account has been deleted'** + String get yourAccountHasBeenDeleted; + + /// Label for verification ID + /// + /// In en, this message translates to: + /// **'Verification ID'** + String get verificationId; + + /// Error message when verification code has expired + /// + /// In en, this message translates to: + /// **'Your verification code has expired'** + String get yourVerificationCodeHasExpired; + + /// Error message when code is incorrect + /// + /// In en, this message translates to: + /// **'Incorrect code'** + String get incorrectCode; + + /// Detailed error message when code is incorrect + /// + /// In en, this message translates to: + /// **'Sorry, the code you\'ve entered is incorrect'** + String get sorryTheCodeYouveEnteredIsIncorrect; + + /// Label for developer settings + /// + /// In en, this message translates to: + /// **'Developer settings'** + String get developerSettings; + + /// Label for server endpoint setting + /// + /// In en, this message translates to: + /// **'Server endpoint'** + String get serverEndpoint; + + /// Error message when endpoint is invalid + /// + /// In en, this message translates to: + /// **'Invalid endpoint'** + String get invalidEndpoint; + + /// Detailed error message when endpoint is invalid + /// + /// In en, this message translates to: + /// **'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'** + String get invalidEndpointMessage; + + /// Success message when endpoint is updated + /// + /// In en, this message translates to: + /// **'Endpoint updated successfully'** + String get endpointUpdatedMessage; } class _StringsLocalizationsDelegate diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart index 99773a8672..cdffa58cc6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ar.dart @@ -103,333 +103,523 @@ class StringsLocalizationsAr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'هل تريد حفظه إلى السعة التخزينية الخاصة بك (مجلد التنزيلات افتراضيا)؟'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'أدخل عنوان بريدك الإلكتروني الجديد'; @override - String get email => 'Email'; + String get email => 'البريد الإلكتروني'; @override - String get verify => 'Verify'; + String get verify => 'التحقق'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'عنوان البريد الإلكتروني غير صالح'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'الرجاء إدخال بريد إلكتروني صالح.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'انتظر قليلاً...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'التحقق من كلمة المرور'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'كلمة المرور غير صحيحة'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'يرجى المحاولة مرة أخرى'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'أدخل كلمة المرور'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'أدخل كلمة المرور الخاصة بك'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'الجلسات النشطة'; @override - String get oops => 'Oops'; + String get oops => 'عذرًا'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'حدث خطأ ما، يرجى المحاولة مرة أخرى'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'سيؤدي هذا إلى تسجيل خروجك من هذا الجهاز:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'إنهاء الجلسة؟'; @override - String get terminate => 'Terminate'; + String get terminate => 'إنهاء'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'هذا الجهاز'; @override - String get createAccount => 'Create account'; + String get createAccount => 'إنشاء حساب'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'ضعيف'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'متوسط'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'قوي'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'إزالة الحساب'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'سوف نأسف لرؤيتك تذهب. هل تواجه بعض المشاكل؟'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'نعم، ارسل الملاحظات'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'لا، حذف الحساب'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => 'الرجاء المصادقة لبدء حذف الحساب'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'تأكيد حذف الحساب'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'هذا الحساب مرتبط بتطبيقات Ente أخرى، إذا كنت تستخدم أحدها.\n\nسنضع موعدا لحذف بياناتك المرفوعة عبر كل تطبيقات Ente، وسيتم حذف حسابك بصورة دائمة.'; @override - String get delete => 'Delete'; + String get delete => 'حذف'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'إنشاء حساب جديد'; @override - String get password => 'Password'; + String get password => 'كلمة المرور'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'تأكيد كلمة المرور'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'قوة كلمة المرور: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'كيف سمعت عن Ente؟ (اختياري)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'نحن لا نتتبع تثبيت التطبيق. سيكون من المفيد إذا أخبرتنا أين وجدتنا!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'أوافق على شروط الخدمة وسياسة الخصوصية'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'الشروط'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'سياسة الخصوصية'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'أنا أفهم أنه إذا فقدت كلمة المرور الخاصة بي، قد أفقد بياناتي لأن بياناتي هي مشفرة من الند للند.'; @override - String get encryption => 'Encryption'; + String get encryption => 'التشفير'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'تسجيل الدخول'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'مرحبًا مجددًا!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'بالنقر على تسجيل الدخول، أوافق على شروط الخدمة و سياسة الخصوصية'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'لا يوجد اتصال بالإنترنت'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'يرجى التحقق من اتصالك بالإنترنت ثم المحاولة من جديد.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'فشل في المصادقة ، يرجى المحاولة مرة أخرى في وقت لاحق'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'إعادة كتابة كلمة المرور'; @override String get recreatePasswordBody => - '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).'; + 'الجهاز الحالي ليس قويًا بما يكفي للتحقق من كلمة المرور الخاصة بك، لذا نحتاج إلى إعادة إنشائها مرة واحدة بطريقة تعمل مع جميع الأجهزة.\n\nالرجاء تسجيل الدخول باستخدام مفتاح الاسترداد وإعادة إنشاء كلمة المرور الخاصة بك (يمكنك استخدام نفس كلمة المرور مرة أخرى إذا كنت ترغب في ذلك).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'استخدم مفتاح الاسترداد'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'هل نسيت كلمة المرور'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'غير البريد الإلكتروني'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'تأكيد البريد الإلكتروني'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'لقد أرسلنا رسالة إلى $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'لإعادة تعيين كلمة المرور الخاصة بك، يرجى التحقق من بريدك الإلكتروني أولاً.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'الرجاء التحقق من صندوق الوارد (والرسائل غير المرغوب فيها) لإكمال التحقق'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'انقر لإدخال الرمز'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'إرسال بريد إلكتروني'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'إعادة إرسال البريد الإلكتروني'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'التحقق ما زال جارٍ'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'انتهت صلاحية الجلسة'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'انتهت صلاحية جلستك. فضلا أعد تسجيل الدخول.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'التحقق من مفتاح المرور'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'بانتظار التحقق...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'حاول مرة أخرى'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'تحقق من الحالة'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => ''; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'إسترجاع الحساب'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'تعيين كلمة المرور'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'تغيير كلمة المرور'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'إعادة تعيين كلمة المرور'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'مفاتيح التشفير'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'أدخل كلمة المرور التي يمكننا استخدامها لتشفير بياناتك'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'أدخل كلمة مرور جديدة يمكننا استخدامها لتشفير بياناتك'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، لا يمكننا فك تشفير بياناتك'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'كيف يعمل'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'توليد مفاتيح التشفير...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'تم تغيير كلمة المرور بنجاح'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'إذا كنت تعتقد أن شخصا ما يعرف كلمة المرور الخاصة بك، يمكنك إجبار جميع الأجهزة الأخرى الستخدمة حاليا لحسابك على تسجيل الخروج.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'تسجيل الخروج من الأجهزة الأخرى'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'لا تقم بتسجيل الخروج'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'توليد مفاتيح التشفير...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'المتابعة'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'جهاز غير آمن'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'عذرًا، لم نتمكن من إنشاء مفاتيح آمنة على هذا الجهاز.\n\nيرجى التسجيل من جهاز مختلف.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => 'تم نسخ عبارة الاسترداد للحافظة'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'مفتاح الاسترداد'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'إذا نسيت كلمة المرور الخاصة بك، فالطريقة الوحيدة التي يمكنك بها استرداد بياناتك هي بهذا المفتاح.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'نحن لا نخزن هذا المفتاح، يرجى حفظ مفتاح الـ 24 كلمة هذا في مكان آمن.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'قم بهذا لاحقاً'; @override - String get saveKey => 'Save key'; + String get saveKey => 'حفظ المفتاح'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => 'حُفِظ مفتاح الاستعادة في مجلد التنزيلات!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'لا يوجد مفتاح استرجاع؟'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'المصادقة الثنائية'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => 'أدخل الرمز المكون من 6 أرقام من\nتطبيق المصادقة'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'جهاز مفقود ؟'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'أدخل رمز الاسترداد'; @override - String get recover => 'Recover'; + String get recover => 'استرداد'; + + @override + String get loggingOut => 'جاري تسجيل الخروج...'; + + @override + String get immediately => 'فورًا'; + + @override + String get appLock => 'قُفْل التطبيق'; + + @override + String get autoLock => 'قفل تلقائي'; + + @override + String get noSystemLockFound => 'لا يوجد قفل نظام'; + + @override + String get deviceLockEnablePreSteps => + 'لتفعيل قُفْل الجهاز، اضبط رمز مرور أو قُفْل الشاشة من الإعدادات'; + + @override + String get appLockDescription => 'اختر نوع قُفْل الشاشة: افتراضي أو مخصص.'; + + @override + String get deviceLock => 'قفل الجهاز'; + + @override + String get pinLock => 'قفل رقم التعريف الشخصي'; + + @override + String get autoLockFeatureDescription => + 'الوقت الذي بعده ينقفل التطبيق بعدما يوضع في الخلفية'; + + @override + String get hideContent => 'أخفِ المحتوى'; + + @override + String get hideContentDescriptionAndroid => + 'يخفي محتوى التطبيق في مبدل التطبيقات ويمنع لقطات الشاشة'; + + @override + String get hideContentDescriptioniOS => + 'يخفي محتوى التطبيق في مبدل التطبيقات'; + + @override + String get tooManyIncorrectAttempts => 'محاولات خاطئة أكثر من المسموح'; + + @override + String get tapToUnlock => 'المس لإلغاء القفل'; + + @override + String get areYouSureYouWantToLogout => + 'هل أنت متأكد من أنك تريد تسجيل الخروج؟'; + + @override + String get yesLogout => 'نعم، تسجيل الخروج'; + + @override + String get authToViewSecrets => + 'الرجاء المصادقة لعرض مفتاح الاسترداد الخاص بك'; + + @override + String get next => 'التالي'; + + @override + String get setNewPassword => 'عين كلمة مرور جديدة'; + + @override + String get enterPin => 'أدخل رقم التعريف الشخصي'; + + @override + String get setNewPin => 'عين رقم تعريف شخصي جديد'; + + @override + String get confirm => 'تأكيد'; + + @override + String get reEnterPassword => 'أعد إدخال كلمة المرور'; + + @override + String get reEnterPin => 'أعد إدخال رقم التعريف الشخصي'; + + @override + String get androidBiometricHint => 'التحقق من الهوية'; + + @override + String get androidBiometricNotRecognized => + 'لم يتم التعرف عليه. حاول مرة أخرى.'; + + @override + String get androidBiometricSuccess => 'تم بنجاح'; + + @override + String get androidCancelButton => 'إلغاء'; + + @override + String get androidSignInTitle => 'المصادقة مطلوبة'; + + @override + String get androidBiometricRequiredTitle => 'البيومترية مطلوبة'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'بيانات اعتماد الجهاز مطلوبة'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'بيانات اعتماد الجهاز مطلوبة'; + + @override + String get goToSettings => 'الانتقال إلى الإعدادات'; + + @override + String get androidGoToSettingsDescription => + 'لم يتم إعداد المصادقة الحيوية على جهازك. انتقل إلى \'الإعدادات > الأمن\' لإضافة المصادقة البيومترية.'; + + @override + String get iOSLockOut => + 'المصادقة البيومترية معطلة. الرجاء قفل الشاشة وفتح القفل لتفعيلها.'; + + @override + String get iOSOkButton => 'حسناً'; + + @override + String get emailAlreadyRegistered => 'البريد الإلكتروني مُسَجَّل من قبل.'; + + @override + String get emailNotRegistered => 'البريد الإلكتروني غير مُسَجَّل.'; + + @override + String get thisEmailIsAlreadyInUse => 'هذا البريد مستخدم مسبقاً'; + + @override + String emailChangedTo(String newEmail) { + return 'تم تغيير البريد الإلكتروني إلى $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'فشلت المصادقة. الرجاء المحاولة مرة أخرى'; + + @override + String get authenticationSuccessful => 'تمت المصادقة بنجاح!'; + + @override + String get sessionExpired => 'انتهت صَلاحِيَة الجِلسة'; + + @override + String get incorrectRecoveryKey => 'مفتاح الاسترداد غير صحيح'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'مفتاح الاسترداد الذي أدخلته غير صحيح'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'تم تحديث المصادقة الثنائية بنجاح'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'انتهت صلاحية رمز التحقق'; + + @override + String get incorrectCode => 'رمز غير صحيح'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'عذراً، الرمز الذي أدخلته غير صحيح'; + + @override + String get developerSettings => 'اعدادات المطور'; + + @override + String get serverEndpoint => 'نقطة طرف الخادم'; + + @override + String get invalidEndpoint => 'نقطة طرف غير صالحة'; + + @override + String get invalidEndpointMessage => + 'عذرا، نقطة الطرف التي أدخلتها غير صالحة. فضلا أدخل نقطة طرف صالحة وأعد المحاولة.'; + + @override + String get endpointUpdatedMessage => 'حُدِّثَت نقطة الطرف بنجاح'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart index 4e50c0742a..a79fe0c723 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_bg.dart @@ -103,333 +103,531 @@ class StringsLocalizationsBg extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Искате ли да запазите това в хранилището си (папка за Изтегляния по подразбиране)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Имейл'; @override - String get verify => 'Verify'; + String get verify => 'Потвърждаване'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Невалиден имейл адрес'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Моля, въведете валиден имейл адрес.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Моля изчакайте...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Потвърдете паролата'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Грешна парола'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Опитайте отново'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Въведете парола'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Въведете паролата си'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активни сесии'; @override - String get oops => 'Oops'; + String get oops => 'Опа'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Нещо се обърка, моля опитайте отново'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Това ще Ви изкара от профила на това устройство!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Това ще Ви изкара от профила на следното устройство:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Прекратяване на сесията?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Прекратяване'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Това устройство'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Създаване на акаунт'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слаба'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Умерена'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Силна'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Изтриване на акаунта'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Ще съжаляваме да си тръгнете. Изправени ли сте пред някакъв проблем?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, изпращане на обратна връзка'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Не, изтриване на акаунта'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Моля, удостоверете се, за да инициирате изтриването на акаунта'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Потвърдете изтриването на акаунта'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Този акаунт е свързан с други приложения на Ente, ако използвате такива.\n\nВашите качени данни във всички приложения на Ente ще бъдат планирани за изтриване и акаунтът Ви ще бъде изтрит за постоянно.'; @override - String get delete => 'Delete'; + String get delete => 'Изтриване'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Създаване на нов акаунт'; @override - String get password => 'Password'; + String get password => 'Парола'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Потвърждаване на паролата'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Сила на паролата: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Как научихте за Ente? (по избор)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Ние не проследяваме инсталиранията на приложения. Ще помогне, ако ни кажете къде ни намерихте!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Съгласявам се с условията за ползване и политиката за поверителност'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Условия'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика за поверителност'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Разбирам, че ако загубя паролата си, може да загубя данните си, тъй като данните ми са шифровани от край до край.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифроване'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Вход'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Добре дошли отново!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'С натискането на вход, се съгласявам с условията за ползване и политиката за поверителност'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Няма връзка с интернет'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Моля, проверете интернет връзката си и опитайте отново.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Неуспешно проверка, моля опитайте отново'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Създайте отново парола'; @override String get recreatePasswordBody => - '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).'; + 'Текущото устройство не е достатъчно мощно, за да потвърди паролата Ви, но можем да я регенерираме по начин, който работи с всички устройства.\n\nМоля, влезте с Вашия ключ за възстановяване и генерирайте отново паролата си (можете да използвате същата отново, ако желаете).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Използвайте ключ за възстановяване'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Забравена парола'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Промяна на имейл'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Потвърдете имейла'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Изпратихме имейл до $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'За да нулирате паролата си, моля, първо потвърдете своя имейл.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Моля, проверете входящата си поща (и спама), за да завършите проверката'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Докоснете, за да въведете код'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Изпратете имейл'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Повторно изпращане на имейл'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Потвърждението все още се изчаква'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сесията изтече'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Вашата сесия изтече. Моля влезте отново.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Удостоверяване с ключ за парола'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Изчаква се потвърждение...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Опитайте отново'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Проверка на състоянието'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Влизане с еднократен код'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Възстановяване на акаунт'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Задаване на парола'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Промяна на паролата'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Нулиране на паролата'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Ключове за шифроване'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Въведете парола, която да използваме за шифроване на Вашите данни'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Въведете нова парола, която да използваме за шифроване на Вашите данни'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Ние не съхраняваме тази парола, така че ако я забравите, не можем да дешифрираме Вашите данни'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Как работи'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Генериране на ключове за шифроване...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Паролата е променена успешно'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Излизане от други устройства'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ако смятате, че някой може да знае паролата Ви, можете да принудите всички други устройства, използващи Вашия акаунт, да излязат.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Излизане от други устройства'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не излизайте'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Генерират се ключове за шифроване...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Продължете'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Несигурно устройство'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'За съжаление не можахме да генерираме защитени ключове на това устройство.\n\nМоля, регистрирайте се от друго устройство.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Ключът за възстановяване е копиран в буферната памет'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Ключ за възстановяване'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ако забравите паролата си, единственият начин да възстановите данните си е с този ключ.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Ние не съхраняваме този ключ, моля, запазете този ключ от 24 думи на сигурно място.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Направете това по-късно'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Запазване на ключа'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Ключът за възстановяване е запазен в папка за Изтегляния!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Няма ключ за възстановяване?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Двуфакторно удостоверяване'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Въведете 6-цифрения код от\nВашето приложение за удостоверяване'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Загубено устройство?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Въведете Вашия ключ за възстановяване'; @override - String get recover => 'Recover'; + String get recover => 'Възстановяване'; + + @override + String get loggingOut => 'Излизане от профила...'; + + @override + String get immediately => 'Незабавно'; + + @override + String get appLock => 'Заключване на приложението'; + + @override + String get autoLock => 'Автоматично заключване'; + + @override + String get noSystemLockFound => 'Не е намерено заключване на системата'; + + @override + String get deviceLockEnablePreSteps => + 'За да активирате заключването на устройството, моля, задайте парола за устройството или заключване на екрана в системните настройки.'; + + @override + String get appLockDescription => + 'Изберете между заключен екран по подразбиране на Вашето устройство и персонализиран заключен екран с ПИН код или парола.'; + + @override + String get deviceLock => 'Заключване на устройството'; + + @override + String get pinLock => 'Заключване с ПИН код'; + + @override + String get autoLockFeatureDescription => + 'Време, след което приложението се заключва, след като е поставено на заден план'; + + @override + String get hideContent => 'Скриване на съдържанието'; + + @override + String get hideContentDescriptionAndroid => + 'Скрива съдържанието на приложението в превключвателя на приложения и деактивира екранните снимки'; + + @override + String get hideContentDescriptioniOS => + 'Скрива съдържанието на приложението в превключвателя на приложения'; + + @override + String get tooManyIncorrectAttempts => 'Твърде много неуспешни опити'; + + @override + String get tapToUnlock => 'Докоснете, за да отключите'; + + @override + String get areYouSureYouWantToLogout => + 'Наистина ли искате да излезете от профила си?'; + + @override + String get yesLogout => 'Да, излез'; + + @override + String get authToViewSecrets => + 'Моля, удостоверете се, за да видите Вашите кодове'; + + @override + String get next => 'Следващ'; + + @override + String get setNewPassword => 'Задаване на нова парола'; + + @override + String get enterPin => 'Въведете ПИН код'; + + @override + String get setNewPin => 'Задаване на нов ПИН код'; + + @override + String get confirm => 'Потвърждаване'; + + @override + String get reEnterPassword => 'Въведете отново паролата'; + + @override + String get reEnterPin => 'Въведете отново ПИН кода'; + + @override + String get androidBiometricHint => 'Потвърждаване на самоличността'; + + @override + String get androidBiometricNotRecognized => + 'Не е разпознат. Опитайте отново.'; + + @override + String get androidBiometricSuccess => 'Успешно'; + + @override + String get androidCancelButton => 'Отказ'; + + @override + String get androidSignInTitle => 'Необходимо е удостоверяване'; + + @override + String get androidBiometricRequiredTitle => 'Изискват се биометрични данни'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Изискват се идентификационни данни за устройството'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Изискват се идентификационни данни за устройството'; + + @override + String get goToSettings => 'Отваряне на настройките'; + + @override + String get androidGoToSettingsDescription => + 'Биометричното удостоверяване не е настроено на Вашето устройство. Отидете на „Настройки > Сигурност“, за да добавите биометрично удостоверяване.'; + + @override + String get iOSLockOut => + 'Биометричното удостоверяване е деактивирано. Моля, заключете и отключете екрана си, за да го активирате.'; + + @override + String get iOSOkButton => 'ОК'; + + @override + String get emailAlreadyRegistered => 'Имейлът вече е регистриран.'; + + @override + String get emailNotRegistered => 'Имейлът не е регистриран.'; + + @override + String get thisEmailIsAlreadyInUse => 'Този имейл вече се използва'; + + @override + String emailChangedTo(String newEmail) { + return 'Имейлът е променен на $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Неуспешно удостоверяване, моля опитайте отново'; + + @override + String get authenticationSuccessful => 'Успешно удостоверяване!'; + + @override + String get sessionExpired => 'Сесията е изтекла'; + + @override + String get incorrectRecoveryKey => 'Неправилен ключ за възстановяване'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Въведеният от Вас ключ за възстановяване е неправилен'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Двуфакторното удостоверяване бе успешно нулирано'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Вашият код за потвърждение е изтекъл'; + + @override + String get incorrectCode => 'Неправилен код'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'За съжаление кодът, който сте въвели, е неправилен'; + + @override + String get developerSettings => 'Настройки за програмисти'; + + @override + String get serverEndpoint => 'Крайна точка на сървъра'; + + @override + String get invalidEndpoint => 'Невалидна крайна точка'; + + @override + String get invalidEndpointMessage => + 'За съжаление въведената от Вас крайна точка е невалидна. Моля, въведете валидна крайна точка и опитайте отново.'; + + @override + String get endpointUpdatedMessage => 'Крайната точка е актуализирана успешно'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart index 0b22dccd2b..f72957d5d5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_cs.dart @@ -103,333 +103,525 @@ class StringsLocalizationsCs extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Chcete toto uložit do paměti zařízení (ve výchozím nastavení do složky Stažené soubory)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Ověřit'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Neplatná e-mailová adresa'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Prosím, zadejte platnou e-mailovou adresu.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Čekejte prosím...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Ověření hesla'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nesprávné heslo'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Zkuste to prosím znovu'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Zadejte heslo'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Zadejte své heslo'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktivní relace'; @override - String get oops => 'Oops'; + String get oops => 'Jejda'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Něco se pokazilo. Zkuste to, prosím, znovu'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Tato akce Vás odhlásí z tohoto zařízení!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Toto Vás odhlásí z následujícího zařízení:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Ukončit relaci?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Ukončit'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Toto zařízení'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Vytvořit účet'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Slabé'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Střední'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silné'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Odstranit účet'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Mrzí nás, že odcházíte. Máte nějaké problémy s aplikací?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ano, poslat zpětnou vazbu'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Ne, odstranit účet'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Pro zahájení odstranění účtu se, prosím, ověřte'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potvrdit odstranění účtu'; @override - String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + String get confirmAccountDeleteMessage => ' '; @override - String get delete => 'Delete'; + String get delete => 'Smazat'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Vytvořit nový účet'; @override - String get password => 'Password'; + String get password => 'Heslo'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potvrzení hesla'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Síla hesla: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Jak jste se dozvěděli o Ente? (volitelné)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Ne sledujeme instalace aplikace. Pomůže nám, když nám sdělíte, kde jste nás našli!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Podmínky'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Podmínky ochrany osobních údajů'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumím, že při zapomenutí hesla mohu ztratit svá data, protože jsou zabezpečena koncovým šifrováním.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifrování'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Přihlásit se'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Vítejte zpět!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Kliknutím na přihlášení souhlasím s podmínkami služby a zásadami ochrany osobních údajů'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Žádné připojení k internetu'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Zkontrolujte, prosím, své připojení k internetu a zkuste to znovu.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Ověření selhalo, přihlaste se, prosím, znovu'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Resetovat heslo'; @override String get recreatePasswordBody => - '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).'; + 'Aktzální zařízení není dostatečně výkonné pro ověření Vašeho hesla, ale můžeme ho regenerovat způsobem, který funguje ve všech zařízením.\n\nPřihlašte se pomocí obnovovacího klíče a znovu si vygenerujte své heslo (můžete použít opět stejné, pokud chcete).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Použít obnovovací klíč'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Zapomenuté heslo'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Změnit e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Ověřit e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Odeslali jsme e-mail na $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Pro obnovení hesla obnovte, prosím, nejprve svůj e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Pro dokončení ověření prosím zkontrolujte, prosím, svou doručenou poštu (a spamy)'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Klepnutím zadejte kód'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Odeslat e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Odeslat e-mail znovu'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Ověřování stále probíhá'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Relace vypršela'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Vaše relace vypršela. Přihlaste se, prosím, znovu.'; @override String get passkeyAuthTitle => 'Passkey verification'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Čekání na ověření...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Zkusit znovu'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Zkontrolovat stav'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Přihlášení s TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Obnovit účet'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nastavit heslo'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Změnit heslo'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Obnovit heslo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifrovací klíče'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Zadejte heslo, kterým můžeme zašifrovat Vaše data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Zadejte nové heslo, kterým můžeme šifrovat Vaše data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vaše heslo neuchováváme. Pokud ho zapomenete, nemůžeme Vaše data dešifrovat'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Jak to funguje'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generování šifrovacích klíčů...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Heslo úspěšně změněno'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Odhlásit z ostatních zařízení'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Pokud si myslíte, že by někdo mohl znát Vaše heslo, můžete vynutit odhlášení ostatních zařízení používajících Váš účet.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Odhlásit z ostatních zařízení'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neodhlašovat'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generování šifrovacích klíčů...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Pokračovat'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Nezabezpečené zařízení'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Omlouváme se, na tomto zařízení nemůžeme vygenerovat bezpečné klíče.\n\nprosím přihlaste se z jiného zařízení.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => 'Obnovovací klíč byl zkopírován'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Obnovovací klíč'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Tento klíč je jedinou cestou pro obnovení Vašich dat, pokud zapomenete heslo.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Tento 24místný klíč neuchováváme, uschovejte ho, prosím, na bezpečném místě.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Udělat později'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Uložit klíč'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Obnovovací klíč uložen do složky Stažené soubory!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Nemáte obnovovací klíč?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvoufaktorové ověření'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Zadejte 6místný kód ze své autentizační aplikace'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Ztratili jste zařízení?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Zadejte svůj obnovovací klíč'; @override - String get recover => 'Recover'; + String get recover => 'Obnovit'; + + @override + String get loggingOut => 'Odhlašování...'; + + @override + String get immediately => 'Ihned'; + + @override + String get appLock => 'Zámek aplikace'; + + @override + String get autoLock => 'Automatické zamykání'; + + @override + String get noSystemLockFound => 'Zámek systému nenalezen'; + + @override + String get deviceLockEnablePreSteps => + 'Pro aktivaci zámku zařízení si nastavte přístupový kód zařízení nebo zámek obrazovky v nastavení systému.'; + + @override + String get appLockDescription => + 'Vyberte si mezi zámkem obrazovky svého zařízení a vlastním zámkem obrazovky s PIN kódem nebo heslem.'; + + @override + String get deviceLock => 'Zámek zařízení'; + + @override + String get pinLock => 'Uzamčení na PIN'; + + @override + String get autoLockFeatureDescription => + 'Interval, po kterém se aplikace běžící na pozadí uzamkne'; + + @override + String get hideContent => 'Skrýt obsah'; + + @override + String get hideContentDescriptionAndroid => 'Skryje obsah aplikace ve '; + + @override + String get hideContentDescriptioniOS => + 'Skryje obsah aplikace při přepínání úloh'; + + @override + String get tooManyIncorrectAttempts => 'Příliš mnoho neúspěšných pokusů'; + + @override + String get tapToUnlock => 'Pro odemčení klepněte'; + + @override + String get areYouSureYouWantToLogout => 'Opravdu se chcete odhlásit?'; + + @override + String get yesLogout => 'Ano, odhlásit se'; + + @override + String get authToViewSecrets => + 'Pro zobrazení svých tajných údajů se musíte ověřit'; + + @override + String get next => 'Další'; + + @override + String get setNewPassword => 'Nastavit nové heslo'; + + @override + String get enterPin => 'Zadejte PIN'; + + @override + String get setNewPin => 'Nadra'; + + @override + String get confirm => 'Potvrdit'; + + @override + String get reEnterPassword => 'Zadejte heslo znovu'; + + @override + String get reEnterPin => 'Zadejte PIN znovu'; + + @override + String get androidBiometricHint => 'Ověřte svou identitu'; + + @override + String get androidBiometricNotRecognized => 'Nerozpoznáno. Zkuste znovu.'; + + @override + String get androidBiometricSuccess => 'Úspěch'; + + @override + String get androidCancelButton => 'Zrušit'; + + @override + String get androidSignInTitle => 'Je požadováno ověření'; + + @override + String get androidBiometricRequiredTitle => + 'Je požadováno biometrické ověření'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Jsou vyžadovány přihlašovací údaje zařízení'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Jsou vyžadovány přihlašovací údaje zařízení'; + + @override + String get goToSettings => 'Jít do nastavení'; + + @override + String get androidGoToSettingsDescription => + 'Na Vašem zařízení není nastaveno biometrické ověřování. Pro aktivaci běžte do \'Nastavení > Zabezpečení\'.'; + + @override + String get iOSLockOut => + 'Biometrické ověřování není povoleno. Pro povolení zamkněte a odemkněte obrazovku.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'E-mail je již registrován.'; + + @override + String get emailNotRegistered => 'E-mail není registrován.'; + + @override + String get thisEmailIsAlreadyInUse => 'Tento e-mail je již používán'; + + @override + String emailChangedTo(String newEmail) { + return 'E-mail změněn na $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Ověření selhalo, zkuste to, prosím, znovu'; + + @override + String get authenticationSuccessful => 'Ověření bylo úspěšné!'; + + @override + String get sessionExpired => 'Relace vypršela'; + + @override + String get incorrectRecoveryKey => 'Nesprávný obnovovací klíč'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Vámi zadaný obnovovací klíč je nesprávný'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Dvoufázové ověření bylo úspěšně obnoveno'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'Váš ověřovací kód vypršel'; + + @override + String get incorrectCode => 'Nesprávný kód'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Omlouváme se, zadaný kód je nesprávný'; + + @override + String get developerSettings => 'Nastavení pro vývojáře'; + + @override + String get serverEndpoint => 'Koncový bod serveru'; + + @override + String get invalidEndpoint => 'Neplatný koncový bod'; + + @override + String get invalidEndpointMessage => + 'Zadaný koncový bod je neplatný. Zadejte prosím platný koncový bod a zkuste to znovu.'; + + @override + String get endpointUpdatedMessage => 'Koncový bod byl úspěšně aktualizován'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart index f641229b24..82f2d03070 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_da.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_da.dart @@ -103,7 +103,7 @@ class StringsLocalizationsDa extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Vil du gemme på din enhed (Downloads mappe som udgangspunkt)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,324 +112,518 @@ class StringsLocalizationsDa extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Bekræft'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ugyldig email adresse'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Indtast en gyldig email adresse.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vent venligst...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bekræft adgangskode'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Forkert adgangskode'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Forsøg venligst igen'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Indtast adgangskode'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Indtast adgangskode'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktive sessioner'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Noget gik galt, forsøg venligst igen'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Dette vil logge dig ud af denne enhed!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Dette vil logge dig ud af den følgende enhed:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Afslut session?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Afslut'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Denne enhed'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Opret konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Svagt'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Middel'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stærkt'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Slet konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Vi er kede af at se dig gå. Er du stødt på et problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, send feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nej, slet konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Bekræft venligst for at påbegynde sletning af konto'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Bekræft sletning af konto'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Denne konto er forbundet til andre Ente apps, hvis du benytter nogle.\n\nDine uploadede data for alle Ente apps vil blive slettet, og din konto vil blive slettet permanent.'; @override - String get delete => 'Delete'; + String get delete => 'Slet'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Opret konto'; @override - String get password => 'Password'; + String get password => 'Kodeord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Bekræft kodeord'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Kodeordets styrke: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hvordan hørte du om Ente? (valgfrit)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Vi tracker ikke app installeringer. Det ville hjælpe os at vide hvordan du fandt os!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Jeg er enig i betingelser for brug og privatlivspolitik'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Betingelser'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privatlivspolitik'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Jeg forstår at hvis jeg mister min adgangskode kan jeg miste mine data, da mine data er end-to-end krypteret.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kryptering'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Log ind'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Velkommen tilbage!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Ved at logge ind godkender jeg Ente\'s betingelser for brug og privatlivspolitik.'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Ingen internetforbindelse'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Tjek venligst din internetforbindelse og forsøg igen.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Bekræftelse fejlede, forsøg venligst igen'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Gendan adgangskode'; @override String get recreatePasswordBody => - '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).'; + 'Denne enhed er ikke kraftfuld nok til at bekræfte adgangskoden, men vi kan gendanne den på en måde der fungerer for alle enheder.\n\nLog venligst ind med din gendannelsesnøgle og gendan din adgangskode (du kan bruge den samme adgangskode igen hvis du ønsker).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Brug gendannelsesnøgle'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Glemt adgangskode'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Skift email adresse'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Bekræft email adresse'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Vi har sendt en email til $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'For at nulstille din adgangskode, bekræft venligst din email adresse.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Tjek venligst din indboks (og spam) for at færdiggøre verificeringen'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tryk for at indtaste kode'; @override String get sendEmail => 'Send email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Send email igen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Bekræftelse afventes stadig'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Session udløbet'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Din session er udløbet. Log venligst på igen.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Bekræftelse af adgangskode'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Venter på bekræftelse...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Forsøg igen'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Tjek status'; @override String get loginWithTOTP => 'Login with TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Gendan konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Angiv adgangskode'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Skift adgangskode'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Nulstil adgangskode'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Krypteringsnøgler'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Indtast en adgangskode vi kan bruge til at kryptere dine data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Indtast en ny adgangskode vi kan bruge til at kryptere dine data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vi gemmer ikke denne adgangskode, så hvis du glemmer den kan vi ikke dekryptere dine data'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Sådan fungerer det'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Genererer krypteringsnøgler...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Adgangskoden er blevet ændret'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Log ud af andre enheder'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Hvis du mistænker at nogen kender din adgangskode kan du tvinge alle enheder der benytter din konto til at logge ud.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Log ud af andre enheder'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Log ikke ud'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Genererer krypteringsnøgler...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Fortsæt'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Usikker enhed'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Beklager, vi kunne ikke generere sikre krypteringsnøgler på denne enhed.\n\nForsøg venligst at oprette en konto fra en anden enhed.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Gendannelsesnøgle kopieret til udklipsholderen'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Gendannelsesnøgle'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Hvis du glemmer dit kodeord er gendannelsesnøglen den eneste mulighed for at få adgang til dine data.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Vi gemmer ikke denne nøgle, gem venligst denne 24-ords nøgle et sikkert sted.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Gør det senere'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Gem nøgle'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Gendannelsesnøgle gemt i din Downloads mappe!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Ingen gendannelsesnøgle?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tofaktorgodkendelse'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Indtast den 6-cifrede kode fra din authenticator app'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Mistet enhed?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Indtast din gendannelsesnøgle'; @override - String get recover => 'Recover'; + String get recover => 'Gendan'; + + @override + String get loggingOut => 'Logger ud...'; + + @override + String get immediately => 'Med det samme'; + + @override + String get appLock => 'Låsning af app'; + + @override + String get autoLock => 'Automatisk lås'; + + @override + String get noSystemLockFound => 'Ingen systemlås fundet'; + + @override + String get deviceLockEnablePreSteps => + 'For at aktivere enhedslås, indstil venligst kode eller skærmlås på din enhed i dine systemindstillinger.'; + + @override + String get appLockDescription => + 'Vælg mellem din enheds standard skærmlås eller skærmlås med pinkode eller adgangskode.'; + + @override + String get deviceLock => 'Enhedslås'; + + @override + String get pinLock => 'Låsning med pinkode'; + + @override + String get autoLockFeatureDescription => + 'Tid til låsning af app efter at være blevet placeret i baggrunden'; + + @override + String get hideContent => 'Skjul indhold'; + + @override + String get hideContentDescriptionAndroid => + 'Skjul app indhold i app-vælger og deaktiver screenshots'; + + @override + String get hideContentDescriptioniOS => 'Skjul app indhold i app-vælger'; + + @override + String get tooManyIncorrectAttempts => 'For mange forkerte forsøg'; + + @override + String get tapToUnlock => 'Tryk for at låse op'; + + @override + String get areYouSureYouWantToLogout => 'Er du sikker på at du vil logge ud?'; + + @override + String get yesLogout => 'Ja, log ud'; + + @override + String get authToViewSecrets => + 'Bekræft venligst din identitet for at se dine hemmeligheder'; + + @override + String get next => 'Næste'; + + @override + String get setNewPassword => 'Indstil ny adgangskode'; + + @override + String get enterPin => 'Indtast pinkode'; + + @override + String get setNewPin => 'Indstil ny pinkode'; + + @override + String get confirm => 'Bekræft'; + + @override + String get reEnterPassword => 'Indtast adgangskode igen'; + + @override + String get reEnterPin => 'Indtast pinkode igen'; + + @override + String get androidBiometricHint => 'Bekræft identitet'; + + @override + String get androidBiometricNotRecognized => 'Ikke genkendt. Forsøg igen.'; + + @override + String get androidBiometricSuccess => 'Succes'; + + @override + String get androidCancelButton => 'Afbryd'; + + @override + String get androidSignInTitle => 'Godkendelse påkrævet'; + + @override + String get androidBiometricRequiredTitle => 'Biometri påkrævet'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Enhedsoplysninger påkrævet'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Enhedsoplysninger påkrævet'; + + @override + String get goToSettings => 'Gå til indstillinger'; + + @override + String get androidGoToSettingsDescription => + 'Biometrisk godkendelse er ikke indstillet på din enhed. Gå til \"Indstillinger > Sikkerhed\" for at indstille biometrisk godkendelse.'; + + @override + String get iOSLockOut => + 'Biometrisk godkendelse er slået fra. Lås din skærm, og lås den derefter op for at aktivere det.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Denne email adresse er allerede i brug'; + + @override + String emailChangedTo(String newEmail) { + return 'Email adresse ændret til $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Bekræftelse af identitet fejlede, forsøg venligst igen'; + + @override + String get authenticationSuccessful => 'Bekræftelse af identitet lykkedes!'; + + @override + String get sessionExpired => 'Session udløbet'; + + @override + String get incorrectRecoveryKey => 'Forkert gendannelsesnøgle'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Den indtastede gendannelsesnøgle er ikke korrekt'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Tofaktorgodkendelse nulstillet'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Din bekræftelseskode er udløbet'; + + @override + String get incorrectCode => 'Forkert kode'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Beklager, den indtastede kode er forkert'; + + @override + String get developerSettings => 'Udvikler-indstillinger'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Ugyldigt endpoint'; + + @override + String get invalidEndpointMessage => + 'Beklager, det indtastede endpoint er ugyldigt. Indtast venligst et gyldigt endpoint og forsøg igen.'; + + @override + String get endpointUpdatedMessage => 'Endpoint opdateret'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart index 3ffe0be49b..f2af902f83 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_el.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_el.dart @@ -103,7 +103,7 @@ class StringsLocalizationsEl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Θέλετε να το αποθηκεύσετε στον αποθηκευτικό σας χώρο (φάκελος Λήψεις από προεπιλογή);'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,324 +112,526 @@ class StringsLocalizationsEl extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Επαλήθευση'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Μη έγκυρη διεύθυνση email'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Παρακαλώ περιμένετε…'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Επαλήθευση κωδικού πρόσβασης'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Λάθος κωδικός πρόσβασης'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Παρακαλώ δοκιμάστε ξανά'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Εισάγετε κωδικό πρόσβασης'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Εισάγετε τον κωδικό πρόσβασης σας'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Ενεργές συνεδρίες'; @override - String get oops => 'Oops'; + String get oops => 'Ουπς'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Κάτι πήγε στραβά, παρακαλώ προσπαθήστε ξανά'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Αυτό θα σας αποσυνδέσει από αυτή τη συσκευή!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Αυτό θα σας αποσυνδέσει από την ακόλουθη συσκευή:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Τερματισμός συνεδρίας;'; @override - String get terminate => 'Terminate'; + String get terminate => 'Τερματισμός'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Αυτή η συσκευή'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Δημιουργία λογαριασμού'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Αδύναμος'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Μέτριος'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Δυνατός'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Διαγραφή λογαριασμού'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Λυπόμαστε που σας βλέπουμε να φεύγετε. Αντιμετωπίζετε κάποιο πρόβλημα;'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ναι, αποστολή σχολίων'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Όχι, διαγραφή λογαριασμού'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να ξεκινήσετε τη διαγραφή λογαριασμού'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Επιβεβαίωση διαγραφής λογαριασμού'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Αυτός ο λογαριασμός είναι συνδεδεμένος με άλλες εφαρμογές Ente, εάν χρησιμοποιείτε κάποια.\n\nΤα δεδομένα σας, σε όλες τις εφαρμογές Ente, θα προγραμματιστούν για διαγραφή και ο λογαριασμός σας θα διαγραφεί οριστικά.'; @override - String get delete => 'Delete'; + String get delete => 'Διαγραφή'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Δημιουργία νέου λογαριασμού'; @override - String get password => 'Password'; + String get password => 'Κωδικόs πρόσβασης'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Επιβεβαίωση κωδικού πρόσβασης'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Ισχύς κωδικού πρόσβασης: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Πώς ακούσατε για το Ente; (προαιρετικό)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Δεν παρακολουθούμε εγκαταστάσεις εφαρμογών. Θα βοηθούσε αν μας είπατε πού μας βρήκατε!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Όροι'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Πολιτική Απορρήτου'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Καταλαβαίνω ότι αν χάσω τον κωδικό μου μπορεί να χάσω τα δεδομένα μου αφού τα δεδομένα μου είναι από άκρο-σε-άκρο κρυπτογραφημένα.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kρυπτογράφηση'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Σύνδεση'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Καλωσορίσατε και πάλι!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Κάνοντας κλικ στη σύνδεση, συμφωνώ με τους όρους χρήσης και την πολιτική απορρήτου'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Χωρίς σύνδεση στο διαδίκτυο'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Παρακαλούμε ελέγξτε τη σύνδεσή σας στο διαδίκτυο και προσπαθήστε ξανά.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Η επαλήθευση απέτυχε, παρακαλώ προσπαθήστε ξανά'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Επαναδημιουργία κωδικού πρόσβασης'; @override String get recreatePasswordBody => - '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).'; + 'Η τρέχουσα συσκευή δεν είναι αρκετά ισχυρή για να επαληθεύσει τον κωδικό πρόσβασής σας, αλλά μπορούμε να τον αναδημιουργήσουμε με έναν τρόπο που λειτουργεί με όλες τις συσκευές.\n\nΠαρακαλούμε συνδεθείτε χρησιμοποιώντας το κλειδί ανάκτησης και αναδημιουργήστε τον κωδικό πρόσβασής σας (μπορείτε να χρησιμοποιήσετε ξανά τον ίδιο αν το επιθυμείτε).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Χρήση κλειδιού ανάκτησης'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Ξέχασα τον κωδικό πρόσβασης σας'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Αλλαγή email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Επαλήθευση email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Έχουμε στείλει ένα μήνυμα στο $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Για να επαναφέρετε τον κωδικό πρόσβασής σας, επαληθεύστε πρώτα το email σας.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Παρακαλώ ελέγξτε τα εισερχόμενά σας (και τα ανεπιθύμητα) για να ολοκληρώσετε την επαλήθευση'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Πατήστε για να εισάγετε τον κωδικό'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Αποστολή email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Επανάληψη αποστολής email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'Η επαλήθευση εξακολουθεί να εκκρεμεί'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Η συνεδρία έληξε'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Η συνεδρία σας έληξε. Παρακαλώ συνδεθείτε ξανά.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Επιβεβαίωση κλειδιού πρόσβασης'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Αναμονή για επαλήθευση...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Προσπαθήστε ξανά'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Έλεγχος κατάστασης'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Είσοδος με TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Ανάκτηση λογαριασμού'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ορισμός κωδικού πρόσβασης'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Αλλαγή κωδικού πρόσβασής'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Επαναφορά κωδικού πρόσβασης'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Κλειδιά κρυπτογράφησης'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Εισάγετε έναν κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για την κρυπτογράφηση των δεδομένων σας'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Εισάγετε ένα νέο κωδικό πρόσβασης που μπορούμε να χρησιμοποιήσουμε για να κρυπτογραφήσουμε τα δεδομένα σας'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Δεν αποθηκεύουμε αυτόν τον κωδικό πρόσβασης, οπότε αν τον ξεχάσετε δεν μπορούμε να αποκρυπτογραφήσουμε τα δεδομένα σας'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Πώς λειτουργεί'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Δημιουργία κλειδιών κρυπτογράφησης...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => + 'Ο κωδικός πρόσβασης άλλαξε επιτυχώς'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Αποσύνδεση από άλλες συσκευές'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Αν νομίζετε ότι κάποιος μπορεί να γνωρίζει τον κωδικό πρόσβασής σας, μπορείτε να αναγκάσετε όλες τις άλλες συσκευές που χρησιμοποιούν το λογαριασμό σας να αποσυνδεθούν.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Αποσύνδεση άλλων συσκευών'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Μην αποσυνδεθείτε'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Δημιουργία κλειδιών κρυπτογράφησης…'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Συνέχεια'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Μη ασφαλής συσκευή'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Συγγνώμη, δεν μπορέσαμε να δημιουργήσουμε ασφαλή κλειδιά σε αυτήν τη συσκευή.\n\nπαρακαλώ εγγραφείτε από μια διαφορετική συσκευή.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Το κλειδί ανάκτησης αντιγράφηκε στο πρόχειρο'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Κλειδί ανάκτησης'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Εάν ξεχάσετε τον κωδικό πρόσβασής σας, ο μόνος τρόπος για να ανακτήσετε τα δεδομένα σας είναι με αυτό το κλειδί.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Δεν αποθηκεύουμε αυτό το κλειδί, παρακαλώ αποθηκεύστε αυτό το κλειδί 24 λέξεων σε μια ασφαλή τοποθεσία.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Κάντε το αργότερα'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Αποθήκευση κλειδιού'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Το κλειδί ανάκτησης αποθηκεύτηκε στο φάκελο Λήψεις!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Χωρίς κλειδί ανάκτησης;'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Αυθεντικοποίηση δύο παραγόντων'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Εισάγετε τον 6ψήφιο κωδικό από \nτην εφαρμογή αυθεντικοποίησης'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Χαμένη συσκευή;'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Εισάγετε το κλειδί ανάκτησης σας'; @override - String get recover => 'Recover'; + String get recover => 'Ανάκτηση'; + + @override + String get loggingOut => 'Αποσύνδεση…'; + + @override + String get immediately => 'Άμεσα'; + + @override + String get appLock => 'Κλείδωμα εφαρμογής'; + + @override + String get autoLock => 'Αυτόματο κλείδωμα'; + + @override + String get noSystemLockFound => 'Δεν βρέθηκε κλείδωμα συστήματος'; + + @override + String get deviceLockEnablePreSteps => + 'Για να ενεργοποιήσετε το κλείδωμα της συσκευής, παρακαλώ ρυθμίστε τον κωδικό πρόσβασης της συσκευής ή το κλείδωμα οθόνης στις ρυθμίσεις του συστήματός σας.'; + + @override + String get appLockDescription => + 'Επιλέξτε ανάμεσα στην προεπιλεγμένη οθόνη κλειδώματος της συσκευής σας και σε μια προσαρμοσμένη οθόνη κλειδώματος με ένα PIN ή έναν κωδικό πρόσβασης.'; + + @override + String get deviceLock => 'Κλείδωμα συσκευής'; + + @override + String get pinLock => 'Κλείδωμα καρφιτσωμάτων'; + + @override + String get autoLockFeatureDescription => + 'Χρόνος μετά τον οποίο η εφαρμογή κλειδώνει μετά την τοποθέτηση στο παρασκήνιο'; + + @override + String get hideContent => 'Απόκρυψη περιεχομένου'; + + @override + String get hideContentDescriptionAndroid => + 'Απόκρυψη περιεχομένου εφαρμογής στην εναλλαγή εφαρμογών και απενεργοποίηση στιγμιότυπων οθόνης'; + + @override + String get hideContentDescriptioniOS => + 'Απόκρυψη περιεχομένου εφαρμογών στην εναλλαγή εφαρμογών'; + + @override + String get tooManyIncorrectAttempts => 'Πάρα πολλές εσφαλμένες προσπάθειες'; + + @override + String get tapToUnlock => 'Πατήστε για ξεκλείδωμα'; + + @override + String get areYouSureYouWantToLogout => + 'Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;'; + + @override + String get yesLogout => 'Ναι, αποσύνδεση'; + + @override + String get authToViewSecrets => + 'Παρακαλώ πραγματοποιήστε έλεγχο ταυτότητας για να δείτε τα μυστικά σας'; + + @override + String get next => 'Επόμενο'; + + @override + String get setNewPassword => 'Ορίστε νέο κωδικό πρόσβασης'; + + @override + String get enterPin => 'Εισαγωγή PIN'; + + @override + String get setNewPin => 'Ορίστε νέο PIN'; + + @override + String get confirm => 'Επιβεβαίωση'; + + @override + String get reEnterPassword => 'Πληκτρολογήστε ξανά τον κωδικό πρόσβασης'; + + @override + String get reEnterPin => 'Πληκτρολογήστε ξανά το PIN'; + + @override + String get androidBiometricHint => 'Επαλήθευση ταυτότητας'; + + @override + String get androidBiometricNotRecognized => + 'Δεν αναγνωρίζεται. Δοκιμάστε ξανά.'; + + @override + String get androidBiometricSuccess => 'Επιτυχία'; + + @override + String get androidCancelButton => 'Ακύρωση'; + + @override + String get androidSignInTitle => 'Απαιτείται έλεγχος ταυτότητας'; + + @override + String get androidBiometricRequiredTitle => 'Απαιτούνται βιομετρικά'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Απαιτούνται στοιχεία συσκευής'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Απαιτούνται στοιχεία συσκευής'; + + @override + String get goToSettings => 'Μετάβαση στις ρυθμίσεις'; + + @override + String get androidGoToSettingsDescription => + 'Η βιομετρική πιστοποίηση δεν έχει ρυθμιστεί στη συσκευή σας. Μεταβείτε στις \'Ρυθμίσεις > Ασφάλεια\' για να προσθέσετε βιομετρική ταυτοποίηση.'; + + @override + String get iOSLockOut => + 'Η βιομετρική ταυτοποίηση είναι απενεργοποιημένη. Παρακαλώ κλειδώστε και ξεκλειδώστε την οθόνη σας για να την ενεργοποιήσετε.'; + + @override + String get iOSOkButton => 'ΟΚ'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'Αυτό το email είναι ήδη σε χρήση'; + + @override + String emailChangedTo(String newEmail) { + return 'Το email άλλαξε σε $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Αποτυχία ελέγχου ταυτότητας, παρακαλώ προσπαθήστε ξανά'; + + @override + String get authenticationSuccessful => 'Επιτυχής έλεγχος ταυτότητας!'; + + @override + String get sessionExpired => 'Η συνεδρία έληξε'; + + @override + String get incorrectRecoveryKey => 'Εσφαλμένο κλειδί ανάκτησης'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Το κλειδί ανάκτησης που εισάγατε είναι εσφαλμένο'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Η αυθεντικοποίηση δύο παραγόντων επαναφέρθηκε επιτυχώς'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Ο κωδικός επαλήθευσης σας έχει λήξει'; + + @override + String get incorrectCode => 'Εσφαλμένος κωδικός'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Λυπούμαστε, ο κωδικός που εισαγάγατε είναι εσφαλμένος'; + + @override + String get developerSettings => 'Ρυθμίσεις προγραμματιστή'; + + @override + String get serverEndpoint => 'Τερματικό σημείο διακομιστή'; + + @override + String get invalidEndpoint => 'Μη έγκυρο τερματικό σημείο'; + + @override + String get invalidEndpointMessage => + 'Λυπούμαστε, το τερματικό σημείο που εισάγατε δεν είναι έγκυρο. Παρακαλώ εισάγετε ένα έγκυρο τερματικό σημείο και προσπαθήστε ξανά.'; + + @override + String get endpointUpdatedMessage => + 'Το τερματκό σημείο ενημερώθηκε επιτυχώς'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart index 018db17f10..37baaf6702 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_en.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_en.dart @@ -432,4 +432,195 @@ class StringsLocalizationsEn extends StringsLocalizations { @override String get recover => 'Recover'; + + @override + String get loggingOut => 'Logging out...'; + + @override + String get immediately => 'Immediately'; + + @override + String get appLock => 'App lock'; + + @override + String get autoLock => 'Auto lock'; + + @override + String get noSystemLockFound => 'No system lock found'; + + @override + String get deviceLockEnablePreSteps => + 'To enable device lock, please setup device passcode or screen lock in your system settings.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Device lock'; + + @override + String get pinLock => 'Pin lock'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Hide content'; + + @override + String get hideContentDescriptionAndroid => + 'Hides app content in the app switcher and disables screenshots'; + + @override + String get hideContentDescriptioniOS => + 'Hides app content in the app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Too many incorrect attempts'; + + @override + String get tapToUnlock => 'Tap to unlock'; + + @override + String get areYouSureYouWantToLogout => 'Are you sure you want to logout?'; + + @override + String get yesLogout => 'Yes, logout'; + + @override + String get authToViewSecrets => 'Please authenticate to view your secrets'; + + @override + String get next => 'Next'; + + @override + String get setNewPassword => 'Set new password'; + + @override + String get enterPin => 'Enter PIN'; + + @override + String get setNewPin => 'Set new PIN'; + + @override + String get confirm => 'Confirm'; + + @override + String get reEnterPassword => 'Re-enter password'; + + @override + String get reEnterPin => 'Re-enter PIN'; + + @override + String get androidBiometricHint => 'Verify identity'; + + @override + String get androidBiometricNotRecognized => 'Not recognized. Try again.'; + + @override + String get androidBiometricSuccess => 'Success'; + + @override + String get androidCancelButton => 'Cancel'; + + @override + String get androidSignInTitle => 'Authentication required'; + + @override + String get androidBiometricRequiredTitle => 'Biometric required'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Device credentials required'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Device credentials required'; + + @override + String get goToSettings => 'Go to settings'; + + @override + String get androidGoToSettingsDescription => + 'Biometric authentication is not set up on your device. Go to \'Settings > Security\' to add biometric authentication.'; + + @override + String get iOSLockOut => + 'Biometric authentication is disabled. Please lock and unlock your screen to enable it.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'This email is already in use'; + + @override + String emailChangedTo(String newEmail) { + return 'Email changed to $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Authentication failed, please try again'; + + @override + String get authenticationSuccessful => 'Authentication successful!'; + + @override + String get sessionExpired => 'Session expired'; + + @override + String get incorrectRecoveryKey => 'Incorrect recovery key'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'The recovery key you entered is incorrect'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Two-factor authentication successfully reset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Your verification code has expired'; + + @override + String get incorrectCode => 'Incorrect code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, the code you\'ve entered is incorrect'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart index 2c9db57cf2..d93fcd07e8 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_es.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_es.dart @@ -103,333 +103,533 @@ class StringsLocalizationsEs extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + '¿Desea guardar el archivo en el almacenamiento (carpeta Descargas por defecto)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Correo electrónico'; @override - String get verify => 'Verify'; + String get verify => 'Verificar'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Dirección de correo electrónico no válida'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Por favor, introduce una dirección de correo electrónico válida.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Por favor, espere...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verificar contraseña'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Contraseña incorrecta'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Por favor, inténtalo de nuevo'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Introduzca la contraseña'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Introduce tu contraseña'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sesiones activas'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Algo ha ido mal, por favor, inténtelo de nuevo'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + '¡Esto cerrará la sesión de este dispositivo!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Esto cerrará la sesión del siguiente dispositivo:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => '¿Terminar sesión?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Terminar'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Este dispositivo'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Crear cuenta'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Poco segura'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Moderada'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Segura'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Eliminar cuenta'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Lamentamos que te vayas. ¿Estás teniendo algún problema?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Sí, enviar comentarios'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'No, eliminar cuenta'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Por favor, autentícate para iniciar la eliminación de la cuenta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmar eliminación de la cuenta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Esta cuenta está vinculada a otras aplicaciones de Ente, si utilizas alguna. \n\nSe programará la eliminación de los datos cargados en todas las aplicaciones de Ente, y tu cuenta se eliminará permanentemente.'; @override - String get delete => 'Delete'; + String get delete => 'Borrar'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Crear cuenta nueva'; @override - String get password => 'Password'; + String get password => 'Contraseña'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmar contraseña'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Fortaleza de la contraseña: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => '¿Cómo conoció Ente? (opcional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'No rastreamos la instalación de las aplicaciones. ¡Nos ayudaría si nos dijera dónde nos encontró!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Estoy de acuerdo con los términos del servicio y la política de privacidad'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Términos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Política de Privacidad'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Entiendo que si pierdo mi contraseña podría perder mis datos, ya que mis datos están cifrados de extremo a extremo.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Cifrado'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Iniciar sesión'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '¡Te damos la bienvenida otra vez!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Al hacer clic en iniciar sesión, acepto los términos de servicio y la política de privacidad'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'No hay conexión a Internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Compruebe su conexión a Internet e inténtelo de nuevo.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verificación fallida, por favor inténtalo de nuevo'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Recrear contraseña'; @override String get recreatePasswordBody => - '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).'; + 'El dispositivo actual no es lo suficientemente potente para verificar su contraseña, pero podemos regenerarla de manera que funcione con todos los dispositivos.\n\nPor favor inicie sesión usando su clave de recuperación y regenere su contraseña (puede volver a utilizar la misma si lo desea).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Usar clave de recuperación'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Olvidé mi contraseña'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Cambiar correo electrónico'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verificar correo electrónico'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Hemos enviado un correo electrónico a $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Para restablecer tu contraseña, por favor verifica tu correo electrónico primero.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Por favor revisa tu bandeja de entrada (y spam) para completar la verificación'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Toca para introducir el código'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Enviar correo electrónico'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Reenviar correo electrónico'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'La verificación todavía está pendiente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'La sesión ha expirado'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Tu sesión ha expirado. Por favor, vuelve a iniciar sesión.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verificación de clave de acceso'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Esperando verificación...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Inténtelo de nuevo'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Comprobar estado'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Inicio de sesión con TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Recuperar cuenta'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Establecer contraseña'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Cambiar contraseña'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Restablecer contraseña'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Claves de cifrado'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Introduzca una contraseña que podamos usar para cifrar sus datos'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Introduzca una contraseña nueva que podamos usar para cifrar sus datos'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'No almacenamos esta contraseña, así que si la olvidas, no podremos descifrar tus datos'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cómo funciona'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generando claves de cifrado...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Contraseña cambiada correctamente'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Cerrar sesión en otros dispositivos'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Si crees que alguien puede conocer tu contraseña, puedes forzar a todos los demás dispositivos que usen tu cuenta a cerrar la sesión.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Cerrar la sesión en otros dispositivos'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'No cerrar la sesión'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generando claves de cifrado...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuar'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Dispositivo inseguro'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Lo sentimos, no hemos podido generar claves seguras en este dispositivo.\n\nRegístrate desde un dispositivo diferente.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Clave de recuperación copiada al portapapeles'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Clave de recuperación'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Si olvidas tu contraseña, la única forma de recuperar tus datos es con esta clave.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nosotros no almacenamos esta clave, por favor guarda esta clave de 24 palabras en un lugar seguro.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Hacer esto más tarde'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Guardar clave'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + '¡Clave de recuperación guardada en la carpeta Descargas!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '¿No tienes la clave de recuperación?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autenticación de dos factores'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Ingrese el código de seis dígitos de su aplicación de autenticación'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '¿Dispositivo perdido?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Introduce tu clave de recuperación'; @override - String get recover => 'Recover'; + String get recover => 'Recuperar'; + + @override + String get loggingOut => 'Cerrando sesión...'; + + @override + String get immediately => 'Inmediatamente'; + + @override + String get appLock => 'Bloqueo de aplicación'; + + @override + String get autoLock => 'Bloqueo automático'; + + @override + String get noSystemLockFound => 'Bloqueo del sistema no encontrado'; + + @override + String get deviceLockEnablePreSteps => + 'Para activar el bloqueo de la aplicación, por favor configura el código de acceso del dispositivo o el bloqueo de pantalla en los ajustes de tu sistema.'; + + @override + String get appLockDescription => + 'Elija entre la pantalla de bloqueo por defecto de su dispositivo y una pantalla de bloqueo personalizada con un PIN o contraseña.'; + + @override + String get deviceLock => 'Bloqueo del dispositivo'; + + @override + String get pinLock => 'Bloqueo con PIN'; + + @override + String get autoLockFeatureDescription => + 'Tiempo tras el cual la aplicación se bloquea después de ser colocada en segundo plano'; + + @override + String get hideContent => 'Ocultar contenido'; + + @override + String get hideContentDescriptionAndroid => + 'Oculta el contenido de la aplicación en el selector de aplicaciones y desactiva las capturas de pantalla'; + + @override + String get hideContentDescriptioniOS => + 'Ocultar el contenido de la aplicación en el selector de aplicaciones'; + + @override + String get tooManyIncorrectAttempts => 'Demasiados intentos incorrectos'; + + @override + String get tapToUnlock => 'Toca para desbloquear'; + + @override + String get areYouSureYouWantToLogout => + '¿Seguro que quieres cerrar la sesión?'; + + @override + String get yesLogout => 'Sí, cerrar la sesión'; + + @override + String get authToViewSecrets => + 'Por favor, autentícate para ver tus secretos'; + + @override + String get next => 'Siguiente'; + + @override + String get setNewPassword => 'Establece una nueva contraseña'; + + @override + String get enterPin => 'Ingresa el PIN'; + + @override + String get setNewPin => 'Establecer nuevo PIN'; + + @override + String get confirm => 'Confirmar'; + + @override + String get reEnterPassword => 'Reescribe tu contraseña'; + + @override + String get reEnterPin => 'Reescribe tu PIN'; + + @override + String get androidBiometricHint => 'Verificar identidad'; + + @override + String get androidBiometricNotRecognized => + 'No reconocido. Inténtalo de nuevo.'; + + @override + String get androidBiometricSuccess => 'Autenticación exitosa'; + + @override + String get androidCancelButton => 'Cancelar'; + + @override + String get androidSignInTitle => 'Se necesita autenticación biométrica'; + + @override + String get androidBiometricRequiredTitle => + 'Se necesita autenticación biométrica'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Se necesitan credenciales de dispositivo'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Se necesitan credenciales de dispositivo'; + + @override + String get goToSettings => 'Ir a Ajustes'; + + @override + String get androidGoToSettingsDescription => + 'La autenticación biométrica no está configurada en tu dispositivo. Ve a \'Ajustes > Seguridad\' para configurar la autenticación biométrica.'; + + @override + String get iOSLockOut => + 'La autenticación biométrica está deshabilitada. Por favor bloquea y desbloquea la pantalla para habilitarla.'; + + @override + String get iOSOkButton => 'Aceptar'; + + @override + String get emailAlreadyRegistered => 'Correo electrónico ya registrado.'; + + @override + String get emailNotRegistered => 'Correo electrónico no registrado.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Este correo electrónico ya está en uso'; + + @override + String emailChangedTo(String newEmail) { + return 'Correo electrónico cambiado a $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Error de autenticación, por favor inténtalo de nuevo'; + + @override + String get authenticationSuccessful => '¡Autenticación exitosa!'; + + @override + String get sessionExpired => 'La sesión ha expirado'; + + @override + String get incorrectRecoveryKey => 'Clave de recuperación incorrecta'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'La clave de recuperación introducida es incorrecta'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Autenticación de doble factor restablecida con éxito'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Tu código de verificación ha expirado'; + + @override + String get incorrectCode => 'Código incorrecto'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Lo sentimos, el código que has introducido es incorrecto'; + + @override + String get developerSettings => 'Ajustes de desarrollador'; + + @override + String get serverEndpoint => 'Endpoint del servidor'; + + @override + String get invalidEndpoint => 'Endpoint no válido'; + + @override + String get invalidEndpointMessage => + 'Lo sentimos, el endpoint introducido no es válido. Por favor, introduce un endpoint válido y vuelve a intentarlo.'; + + @override + String get endpointUpdatedMessage => 'Endpoint actualizado con éxito'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart index 54a58684ad..3375cff00a 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_fr.dart @@ -103,333 +103,536 @@ class StringsLocalizationsFr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Voulez-vous enregistrer ceci sur votre stockage (dossier Téléchargements par défaut) ?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Saisissez votre nouvelle adresse email'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Vérifier'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Adresse e-mail invalide'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Veuillez saisir une adresse e-mail valide.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Veuillez patienter...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Vérifier le mot de passe'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Mot de passe incorrect'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Veuillez réessayer'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Saisissez le mot de passe'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Entrez votre mot de passe'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sessions actives'; @override - String get oops => 'Oops'; + String get oops => 'Oups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Quelque chose s\'est mal passé, veuillez recommencer'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Cela vous déconnectera de cet appareil !'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Cela vous déconnectera de l\'appareil suivant :'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Quitter la session ?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Quitter'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Cet appareil'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Créer un compte'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Faible'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Modéré'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Fort'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Supprimer le compte'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Nous sommes désolés de vous voir partir. Rencontrez-vous un problème ?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Oui, envoyer un commentaire'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Non, supprimer le compte'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Veuillez vous authentifier pour débuter la suppression du compte'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmer la suppression du compte'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Ce compte est lié à d\'autres applications ente, si vous en utilisez une.\n\nVos données téléchargées, dans toutes les applications ente, seront planifiées pour suppression, et votre compte sera définitivement supprimé.'; @override - String get delete => 'Delete'; + String get delete => 'Supprimer'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Créer un nouveau compte'; @override - String get password => 'Password'; + String get password => 'Mot de passe'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmer le mot de passe'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Force du mot de passe : $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => + 'Comment avez-vous entendu parler de Ente? (facultatif)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nous ne suivons pas les installations d\'applications. Il serait utile que vous nous disiez comment vous nous avez trouvés !'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'J\'accepte les conditions d\'utilisation et la politique de confidentialité'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Conditions'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Politique de confidentialité'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Je comprends que si je perds mon mot de passe, je risque de perdre mes données puisque celles-ci sont chiffrées de bout en bout.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Chiffrement'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Connexion'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Bon retour parmi nous !'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'En cliquant sur \"Connexion\", j\'accepte les conditions d\'utilisation et la politique de confidentialité'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Aucune connexion internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Veuillez vérifier votre connexion internet puis réessayer.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'La vérification a échouée, veuillez réessayer'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Recréer le mot de passe'; @override String get recreatePasswordBody => - '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).'; + 'L\'appareil actuel n\'est pas assez puissant pour vérifier votre mot de passe, donc nous avons besoin de le régénérer une fois d\'une manière qu\'il fonctionne avec tous les périphériques.\n\nVeuillez vous connecter en utilisant votre clé de récupération et régénérer votre mot de passe (vous pouvez utiliser le même si vous le souhaitez).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Utiliser la clé de récupération'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Mot de passe oublié'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Modifier l\'e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Vérifier l\'e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Nous avons envoyé un mail à $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Pour réinitialiser votre mot de passe, veuillez d\'abord vérifier votre e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Veuillez consulter votre boîte de courriels (et les spam) pour compléter la vérification'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Appuyez pour entrer un code'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Envoyer un e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Renvoyer le courriel'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => + 'La vérification est toujours en attente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Session expirée'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Votre session a expiré. Veuillez vous reconnecter.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Vérification du code d\'accès'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'En attente de vérification...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Réessayer'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Vérifier le statut'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Se connecter avec un code TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Récupérer un compte'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Définir le mot de passe'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Modifier le mot de passe'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Réinitialiser le mot de passe'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Clés de chiffrement'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Entrez un mot de passe que nous pouvons utiliser pour chiffrer vos données'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Entrez un nouveau mot de passe que nous pouvons utiliser pour chiffrer vos données'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Nous ne stockons pas ce mot de passe. Si vous l\'oubliez, nous ne pourrons pas déchiffrer vos données'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Comment ça fonctionne'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Génération des clés de chiffrement...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => + 'Le mot de passe a été modifié avec succès'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Déconnexion des autres appareils'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Si vous pensez que quelqu\'un connaît peut-être votre mot de passe, vous pouvez forcer tous les autres appareils utilisant votre compte à se déconnecter.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Déconnecter les autres appareils'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Ne pas se déconnecter'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Génération des clés de chiffrement...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuer'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Appareil non sécurisé'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '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.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Clé de récupération copiée dans le presse-papiers'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Clé de récupération'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Si vous oubliez votre mot de passe, la seule façon de récupérer vos données sera grâce à cette clé.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nous ne stockons pas cette clé, veuillez enregistrer cette clé de 24 mots dans un endroit sûr.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Plus tard'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Enregistrer la clé'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Clé de récupération enregistrée dans le dossier Téléchargements !'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Pas de clé de récupération ?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Authentification à deux facteurs'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Entrez le code à 6 chiffres de votre application d\'authentification'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Appareil perdu ?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Saisissez votre clé de récupération'; @override - String get recover => 'Recover'; + String get recover => 'Restaurer'; + + @override + String get loggingOut => 'Deconnexion...'; + + @override + String get immediately => 'Immédiatement'; + + @override + String get appLock => 'Verrouillage d\'application'; + + @override + String get autoLock => 'Verrouillage automatique'; + + @override + String get noSystemLockFound => 'Aucun verrou système trouvé'; + + @override + String get deviceLockEnablePreSteps => + '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.'; + + @override + String get appLockDescription => + 'Choisissez entre l\'écran de verrouillage par défaut de votre appareil et un écran de verrouillage par code PIN ou mot de passe personnalisé.'; + + @override + String get deviceLock => 'Verrouillage de l\'appareil'; + + @override + String get pinLock => 'Verrouillage par code PIN'; + + @override + String get autoLockFeatureDescription => + 'Délai après lequel l\'application se verrouille une fois qu\'elle a été mise en arrière-plan'; + + @override + String get hideContent => 'Masquer le contenu'; + + @override + String get hideContentDescriptionAndroid => + 'Masque le contenu de l\'application sur le menu et désactive les captures d\'écran'; + + @override + String get hideContentDescriptioniOS => + 'Masque le contenu de l\'application sur le menu'; + + @override + String get tooManyIncorrectAttempts => 'Trop de tentatives incorrectes'; + + @override + String get tapToUnlock => 'Appuyer pour déverrouiller'; + + @override + String get areYouSureYouWantToLogout => + 'Êtes-vous sûr de vouloir vous déconnecter ?'; + + @override + String get yesLogout => 'Oui, se déconnecter'; + + @override + String get authToViewSecrets => + 'Veuillez vous authentifier pour voir vos souvenirs'; + + @override + String get next => 'Suivant'; + + @override + String get setNewPassword => 'Définir un nouveau mot de passe'; + + @override + String get enterPin => 'Saisir le code PIN'; + + @override + String get setNewPin => 'Définir un nouveau code PIN'; + + @override + String get confirm => 'Confirmer'; + + @override + String get reEnterPassword => 'Ressaisir le mot de passe'; + + @override + String get reEnterPin => 'Ressaisir le code PIN'; + + @override + String get androidBiometricHint => 'Vérifier l’identité'; + + @override + String get androidBiometricNotRecognized => + 'Non reconnu. Veuillez réessayer.'; + + @override + String get androidBiometricSuccess => 'Parfait'; + + @override + String get androidCancelButton => 'Annuler'; + + @override + String get androidSignInTitle => 'Authentification requise'; + + @override + String get androidBiometricRequiredTitle => 'Empreinte digitale requise'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Identifiants de l\'appareil requis'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Identifiants de l\'appareil requis'; + + @override + String get goToSettings => 'Allez dans les paramètres'; + + @override + String get androidGoToSettingsDescription => + 'L\'authentification biométrique n\'est pas configurée sur votre appareil. Allez dans \'Paramètres > Sécurité\' pour l\'ajouter.'; + + @override + String get iOSLockOut => + 'L\'authentification biométrique est désactivée. Veuillez verrouiller et déverrouiller votre écran pour l\'activer.'; + + @override + String get iOSOkButton => 'Ok'; + + @override + String get emailAlreadyRegistered => 'E-mail déjà enregistré.'; + + @override + String get emailNotRegistered => 'E-mail non enregistré.'; + + @override + String get thisEmailIsAlreadyInUse => 'Cette adresse mail est déjà utilisé'; + + @override + String emailChangedTo(String newEmail) { + return 'L\'e-mail a été changé en $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'L\'authentification a échouée, veuillez réessayer'; + + @override + String get authenticationSuccessful => 'Authentification réussie!'; + + @override + String get sessionExpired => 'Session expirée'; + + @override + String get incorrectRecoveryKey => 'Clé de récupération non valide'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'La clé de récupération que vous avez entrée est incorrecte'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'L\'authentification à deux facteurs a été réinitialisée avec succès '; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Votre code de vérification a expiré'; + + @override + String get incorrectCode => 'Code non valide'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Le code que vous avez saisi est incorrect'; + + @override + String get developerSettings => 'Paramètres du développeur'; + + @override + String get serverEndpoint => 'Point de terminaison serveur'; + + @override + String get invalidEndpoint => 'Point de terminaison non valide'; + + @override + String get invalidEndpointMessage => + 'Désolé, le point de terminaison que vous avez entré n\'est pas valide. Veuillez en entrer un valide puis réessayez.'; + + @override + String get endpointUpdatedMessage => + 'Point de terminaison mis à jour avec succès'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart index baa8ee45e6..e3839340a6 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_id.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_id.dart @@ -103,333 +103,528 @@ class StringsLocalizationsId extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Anda ingin menyimpan kode ke penyimpanan Anda (folder pilihan bawaan adalah folder Downloads)'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Masukkan alamat email baru anda'; @override String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Verifikasi'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Alamat email tidak valid'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Harap masukkan alamat email yang valid.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Mohon tunggu...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verifikasi kata sandi'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Kata sandi salah'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Harap coba lagi'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Masukkan kata sandi'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Masukkan kata sandi Anda'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sesi aktif'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Ada yang salah. Mohon coba kembali'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Langkah ini akan mengeluarkan Anda dari gawai ini!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Langkah ini akan mengeluarkan Anda dari gawai berikut:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Akhiri sesi?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Akhiri'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Gawai ini'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Buat akun'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Lemah'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Sedang'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Kuat'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Hapus akun'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Kami akan merasa kehilangan Anda. Apakah Anda menghadapi masalah?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ya, kirim umpan balik'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Tidak, hapus akun'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Harap autentikasi untuk memulai penghapusan akun'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Konfirmasikan penghapusan akun'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Akun ini terhubung dengan aplikasi Ente yang lain (jika Anda pakai).\n\nData yang Anda unggah di seluruh aplikasi Ente akan dijadwalkan untuk dihapus. Akun Anda juga akan dihapus secara permanen.'; @override - String get delete => 'Delete'; + String get delete => 'Hapus'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Buat akun baru'; @override - String get password => 'Password'; + String get password => 'Kata Sandi'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Konfirmasi kata sandi'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Tingkat kekuatan kata sandi: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Dari mana Anda menemukan Ente? (opsional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Kami tidak melacak penginstalan aplikasi kami. Akan sangat membantu kami bila Anda memberitahu kami dari mana Anda mengetahui Ente!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Ketentuan'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Kebijakan Privasi'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Saya mengerti bahwa jika saya lupa kata sandi saya, data saya dapat hilang karena data saya terenkripsi secara end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Enkripsi'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Masuk akun'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Selamat datang kembali!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Dengan menekan masuk akun, saya menyetujui syarat dan ketentuan serta kebijakan privasi Ente'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Tiada koneksi internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Mohon periksa koneksi internet Anda dan coba kembali.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Gagal memverifikasi. Mohon coba lagi'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Membuat kembali kata sandi'; @override String get recreatePasswordBody => - '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).'; + 'Gawai Anda saat ini tidak dapat memverifikasi kata sandi Anda. Namun, kami dapat membuat ulang dengan cara yang dapat digunakan pada semua gawai.\n\nMohon masuk log dengan kunci pemulihan dan buat ulang kata sandi Anda (kata sandi yang sama diperbolehkan).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Gunakan kunci pemulihan'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Lupa kata sandi'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Ubah alamat email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verifikasi email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Kami telah mengirimkan sebuah posel ke $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Untuk mengatur ulang kata sandi, mohon verifikasi surel Anda terlebih dahulu.'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => 'Mohon cek'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Ketuk untuk memasukkan kode'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Kirim surel'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Kirim ulang surel'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verifikasi tertunda'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sesi sudah berakhir'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Sesi Anda sudah berakhir. Mohon masuk log kembali.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verifikasi passkey'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Menantikan verifikasi...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Coba lagi'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Periksa status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Masuk menggunakan TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Pulihkan akun'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Atur kata sandi'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Ubah kata sandi'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Atur ulang kata sandi'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Kunci enkripsi'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Masukkan kata sandi yang dapat kami gunakan untuk mengenkripsi data Anda'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Masukkan kata sandi baru yang dapat kami gunakan untuk mengenkripsi data Anda'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Kami tidak menyimpan kata sandi Anda. Jika Anda lupa, kami tidak dapat mendekripsi data Anda'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cara kerjanya'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Sedang membuat kunci enkripsi...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Kata sandi sukses diubah'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Keluar dari gawai yang lain'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jika Anda pikir seseorang mungkin mengetahui kata sandi Anda, Anda dapat mengeluarkan akun Anda pada semua gawai'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Keluar akun pada gawai yang lain'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Jangan keluar'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Sedang membuat kunci enkripsi...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Lanjutkan'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Perangkat tidak aman'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Maaf, kami tidak dapat membuat kunci yang aman pada perangkat ini.\n\nHarap mendaftar dengan perangkat lain.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Kunci pemulihan disalin ke papan klip'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kunci pemulihan'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jika Anda lupa kata sandi, satu-satunya cara memulihkan data Anda adalah dengan kunci ini.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Kami tidak menyimpan kunci ini, jadi harap simpan kunci yang berisi 24 kata ini dengan aman.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Lakukan lain kali'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Simpan kunci'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kunci pemulihan sudah tersimpan di folder \'Downloads\'!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Tidak punya kunci pemulihan?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autentikasi dua langkah'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Masukkan kode 6 digit dari aplikasi autentikator Anda'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Perangkat hilang?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Masukkan kunci pemulihan Anda'; @override - String get recover => 'Recover'; + String get recover => 'Pulihkan'; + + @override + String get loggingOut => 'Mengeluarkan akun...'; + + @override + String get immediately => 'Segera'; + + @override + String get appLock => 'Kunci aplikasi'; + + @override + String get autoLock => 'Kunci otomatis'; + + @override + String get noSystemLockFound => 'Tidak ditemukan kunci sistem'; + + @override + String get deviceLockEnablePreSteps => + 'Pasang kunci sandi atau kunci layar pada pengaturan sistem untuk menyalakan Pengunci Gawai.'; + + @override + String get appLockDescription => + 'Pilih layar kunci bawaan gawai Anda ATAU layar kunci kustom dengan PIN atau kata sandi.'; + + @override + String get deviceLock => 'Kunci perangkat'; + + @override + String get pinLock => 'PIN'; + + @override + String get autoLockFeatureDescription => + 'Durasi waktu aplikasi akan terkunci setelah aplikasi ditutup'; + + @override + String get hideContent => 'Sembunyikan isi'; + + @override + String get hideContentDescriptionAndroid => + 'Menyembunyikan konten aplikasi di pemilih aplikasi dan menonaktifkan tangkapan layar'; + + @override + String get hideContentDescriptioniOS => + 'Menyembunyikan konten aplikasi di pemilih aplikasi'; + + @override + String get tooManyIncorrectAttempts => 'Terlalu banyak percobaan yang salah'; + + @override + String get tapToUnlock => 'Ketuk untuk membuka'; + + @override + String get areYouSureYouWantToLogout => + 'Anda yakin ingin keluar dari akun ini?'; + + @override + String get yesLogout => 'Ya, keluar akun'; + + @override + String get authToViewSecrets => + 'Harap lakukan autentikasi untuk melihat rahasia Anda'; + + @override + String get next => 'Selanjutnya'; + + @override + String get setNewPassword => 'Pasang kata sandi baru'; + + @override + String get enterPin => 'Masukkan PIN'; + + @override + String get setNewPin => 'Pasang PIN yang baru'; + + @override + String get confirm => 'Konfirmasikan'; + + @override + String get reEnterPassword => 'Masukkan kembali kata sandi'; + + @override + String get reEnterPin => 'Masukkan kembali PIN'; + + @override + String get androidBiometricHint => 'Verifikasikan identitas Anda'; + + @override + String get androidBiometricNotRecognized => 'Tidak dikenal. Coba lagi.'; + + @override + String get androidBiometricSuccess => 'Sukses'; + + @override + String get androidCancelButton => 'Batalkan'; + + @override + String get androidSignInTitle => 'Autentikasi diperlukan'; + + @override + String get androidBiometricRequiredTitle => 'Biometrik diperlukan'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Kredensial perangkat diperlukan'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Kredensial perangkat diperlukan'; + + @override + String get goToSettings => 'Pergi ke pengaturan'; + + @override + String get androidGoToSettingsDescription => + 'Tidak ada autentikasi biometrik pada gawai Anda. Buka \'Pengaturan > Keamanan\' untuk menambahkan autentikasi biometrik pada gawai Anda.'; + + @override + String get iOSLockOut => + 'Autentikasi biometrik dimatikan. Kunci dan buka layar Anda untuk menyalakan autentikasi biometrik.'; + + @override + String get iOSOkButton => 'Oke'; + + @override + String get emailAlreadyRegistered => 'Email sudah terdaftar.'; + + @override + String get emailNotRegistered => 'Email belum terdaftar.'; + + @override + String get thisEmailIsAlreadyInUse => 'Surel ini sudah dipakai!'; + + @override + String emailChangedTo(String newEmail) { + return 'Surel sudah diganti menjadi $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Gagal mengautentikasi. Mohon coba lagi'; + + @override + String get authenticationSuccessful => 'Sukses mengautentikasi!'; + + @override + String get sessionExpired => 'Sesi berakhir'; + + @override + String get incorrectRecoveryKey => 'Kunci pemulihan takbenar'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Kunci pemulihan yang Anda masukkan takbenar'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Autentikasi dwifaktor sukses diatur ulang'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Kode verifikasi Anda telah kedaluwarsa'; + + @override + String get incorrectCode => 'Kode takbenar'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Maaf, kode yang Anda masukkan takbenar'; + + @override + String get developerSettings => 'Pengaturan Pengembang'; + + @override + String get serverEndpoint => 'Peladen endpoint'; + + @override + String get invalidEndpoint => 'Endpoint takvalid'; + + @override + String get invalidEndpointMessage => + 'Maaf, endpoint yang Anda masukkan takvalid. Mohon masukkan endpoint yang valid, lalu coba kembali.'; + + @override + String get endpointUpdatedMessage => 'Endpoint berhasil diubah'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart index 8947a8aaef..54766bcdb1 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ja.dart @@ -599,334 +599,5 @@ class StringsLocalizationsJa extends StringsLocalizations { '入力されたエンドポイントは無効です。有効なエンドポイントを入力して再試行してください。'; @override - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; - - @override - String get enterNewEmailHint => 'Enter your new email address'; - - @override - String get email => 'Email'; - - @override - String get verify => 'Verify'; - - @override - String get invalidEmailTitle => 'Invalid email address'; - - @override - String get invalidEmailMessage => 'Please enter a valid email address.'; - - @override - String get pleaseWait => 'Please wait...'; - - @override - String get verifyPassword => 'Verify password'; - - @override - String get incorrectPasswordTitle => 'Incorrect password'; - - @override - String get pleaseTryAgain => 'Please try again'; - - @override - String get enterPassword => 'Enter password'; - - @override - String get enterYourPasswordHint => 'Enter your password'; - - @override - String get activeSessions => 'Active sessions'; - - @override - String get oops => 'Oops'; - - @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; - - @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; - - @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; - - @override - String get terminateSession => 'Terminate session?'; - - @override - String get terminate => 'Terminate'; - - @override - String get thisDevice => 'This device'; - - @override - String get createAccount => 'Create account'; - - @override - String get weakStrength => 'Weak'; - - @override - String get moderateStrength => 'Moderate'; - - @override - String get strongStrength => 'Strong'; - - @override - String get deleteAccount => 'Delete account'; - - @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; - - @override - String get yesSendFeedbackAction => 'Yes, send feedback'; - - @override - String get noDeleteAccountAction => 'No, delete account'; - - @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; - - @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; - - @override - String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; - - @override - String get delete => 'Delete'; - - @override - String get createNewAccount => 'Create new account'; - - @override - String get password => 'Password'; - - @override - String get confirmPassword => 'Confirm password'; - - @override - String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; - } - - @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; - - @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; - - @override - String get signUpTerms => - 'I agree to the terms of service and privacy policy'; - - @override - String get termsOfServicesTitle => 'Terms'; - - @override - String get privacyPolicyTitle => 'Privacy Policy'; - - @override - String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; - - @override - String get encryption => 'Encryption'; - - @override - String get logInLabel => 'Log in'; - - @override - String get welcomeBack => 'Welcome back!'; - - @override - String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; - - @override - String get noInternetConnection => 'No internet connection'; - - @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; - - @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; - - @override - String get recreatePasswordTitle => 'Recreate password'; - - @override - String get recreatePasswordBody => - '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).'; - - @override - String get useRecoveryKey => 'Use recovery key'; - - @override - String get forgotPassword => 'Forgot password'; - - @override - String get changeEmail => 'Change email'; - - @override - String get verifyEmail => 'Verify email'; - - @override - String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; - } - - @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; - - @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; - - @override - String get tapToEnterCode => 'Tap to enter code'; - - @override - String get sendEmail => 'Send email'; - - @override - String get resendEmail => 'Resend email'; - - @override - String get passKeyPendingVerification => 'Verification is still pending'; - - @override - String get loginSessionExpired => 'Session expired'; - - @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; - - @override - String get passkeyAuthTitle => 'Passkey verification'; - - @override - String get waitingForVerification => 'Waiting for verification...'; - - @override - String get tryAgain => 'Try again'; - - @override - String get checkStatus => 'Check status'; - - @override - String get loginWithTOTP => 'Login with TOTP'; - - @override - String get recoverAccount => 'Recover account'; - - @override - String get setPasswordTitle => 'Set password'; - - @override - String get changePasswordTitle => 'Change password'; - - @override - String get resetPasswordTitle => 'Reset password'; - - @override - String get encryptionKeys => 'Encryption keys'; - - @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; - - @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; - - @override - String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; - - @override - String get howItWorks => 'How it works'; - - @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; - - @override - String get passwordChangedSuccessfully => 'Password changed successfully'; - - @override - String get signOutFromOtherDevices => 'Sign out from other devices'; - - @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; - - @override - String get signOutOtherDevices => 'Sign out other devices'; - - @override - String get doNotSignOut => 'Do not sign out'; - - @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; - - @override - String get continueLabel => 'Continue'; - - @override - String get insecureDevice => 'Insecure device'; - - @override - String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; - - @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; - - @override - String get recoveryKey => 'Recovery key'; - - @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; - - @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; - - @override - String get doThisLater => 'Do this later'; - - @override - String get saveKey => 'Save key'; - - @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; - - @override - String get noRecoveryKeyTitle => 'No recovery key?'; - - @override - String get twoFactorAuthTitle => 'Two-factor authentication'; - - @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; - - @override - String get lostDeviceTitle => 'Lost device?'; - - @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; - - @override - String get recover => 'Recover'; + String get endpointUpdatedMessage => 'エンドポイントの更新に成功しました'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart index 5286fa2fe0..bdb0abf69e 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ko.dart @@ -601,334 +601,4 @@ class StringsLocalizationsKo extends StringsLocalizations { @override String get endpointUpdatedMessage => '엔드포인트가 성공적으로 업데이트됨'; - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; - - @override - String get enterNewEmailHint => 'Enter your new email address'; - - @override - String get email => 'Email'; - - @override - String get verify => 'Verify'; - - @override - String get invalidEmailTitle => 'Invalid email address'; - - @override - String get invalidEmailMessage => 'Please enter a valid email address.'; - - @override - String get pleaseWait => 'Please wait...'; - - @override - String get verifyPassword => 'Verify password'; - - @override - String get incorrectPasswordTitle => 'Incorrect password'; - - @override - String get pleaseTryAgain => 'Please try again'; - - @override - String get enterPassword => 'Enter password'; - - @override - String get enterYourPasswordHint => 'Enter your password'; - - @override - String get activeSessions => 'Active sessions'; - - @override - String get oops => 'Oops'; - - @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; - - @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; - - @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; - - @override - String get terminateSession => 'Terminate session?'; - - @override - String get terminate => 'Terminate'; - - @override - String get thisDevice => 'This device'; - - @override - String get createAccount => 'Create account'; - - @override - String get weakStrength => 'Weak'; - - @override - String get moderateStrength => 'Moderate'; - - @override - String get strongStrength => 'Strong'; - - @override - String get deleteAccount => 'Delete account'; - - @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; - - @override - String get yesSendFeedbackAction => 'Yes, send feedback'; - - @override - String get noDeleteAccountAction => 'No, delete account'; - - @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; - - @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; - - @override - String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; - - @override - String get delete => 'Delete'; - - @override - String get createNewAccount => 'Create new account'; - - @override - String get password => 'Password'; - - @override - String get confirmPassword => 'Confirm password'; - - @override - String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; - } - - @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; - - @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; - - @override - String get signUpTerms => - 'I agree to the terms of service and privacy policy'; - - @override - String get termsOfServicesTitle => 'Terms'; - - @override - String get privacyPolicyTitle => 'Privacy Policy'; - - @override - String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; - - @override - String get encryption => 'Encryption'; - - @override - String get logInLabel => 'Log in'; - - @override - String get welcomeBack => 'Welcome back!'; - - @override - String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; - - @override - String get noInternetConnection => 'No internet connection'; - - @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; - - @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; - - @override - String get recreatePasswordTitle => 'Recreate password'; - - @override - String get recreatePasswordBody => - '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).'; - - @override - String get useRecoveryKey => 'Use recovery key'; - - @override - String get forgotPassword => 'Forgot password'; - - @override - String get changeEmail => 'Change email'; - - @override - String get verifyEmail => 'Verify email'; - - @override - String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; - } - - @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; - - @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; - - @override - String get tapToEnterCode => 'Tap to enter code'; - - @override - String get sendEmail => 'Send email'; - - @override - String get resendEmail => 'Resend email'; - - @override - String get passKeyPendingVerification => 'Verification is still pending'; - - @override - String get loginSessionExpired => 'Session expired'; - - @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; - - @override - String get passkeyAuthTitle => 'Passkey verification'; - - @override - String get waitingForVerification => 'Waiting for verification...'; - - @override - String get tryAgain => 'Try again'; - - @override - String get checkStatus => 'Check status'; - - @override - String get loginWithTOTP => 'Login with TOTP'; - - @override - String get recoverAccount => 'Recover account'; - - @override - String get setPasswordTitle => 'Set password'; - - @override - String get changePasswordTitle => 'Change password'; - - @override - String get resetPasswordTitle => 'Reset password'; - - @override - String get encryptionKeys => 'Encryption keys'; - - @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; - - @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; - - @override - String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; - - @override - String get howItWorks => 'How it works'; - - @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; - - @override - String get passwordChangedSuccessfully => 'Password changed successfully'; - - @override - String get signOutFromOtherDevices => 'Sign out from other devices'; - - @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; - - @override - String get signOutOtherDevices => 'Sign out other devices'; - - @override - String get doNotSignOut => 'Do not sign out'; - - @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; - - @override - String get continueLabel => 'Continue'; - - @override - String get insecureDevice => 'Insecure device'; - - @override - String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; - - @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; - - @override - String get recoveryKey => 'Recovery key'; - - @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; - - @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; - - @override - String get doThisLater => 'Do this later'; - - @override - String get saveKey => 'Save key'; - - @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; - - @override - String get noRecoveryKeyTitle => 'No recovery key?'; - - @override - String get twoFactorAuthTitle => 'Two-factor authentication'; - - @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; - - @override - String get lostDeviceTitle => 'Lost device?'; - - @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; - - @override - String get recover => 'Recover'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart index 2d4a43e317..67514db54f 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_lt.dart @@ -103,333 +103,528 @@ class StringsLocalizationsLt extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Ar norite tai išsaugoti savo saugykloje (pagal numatytuosius nustatymus – atsisiuntimų aplanke)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'El. paštas'; @override - String get verify => 'Verify'; + String get verify => 'Patvirtinti'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Netinkamas el. pašto adresas'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Įveskite tinkamą el. pašto adresą.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Palaukite...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Patvirtinkite slaptažodį'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Neteisingas slaptažodis.'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Bandykite dar kartą.'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Įveskite slaptažodį'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Įveskite savo slaptažodį'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktyvūs seansai'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Kažkas nutiko ne taip. Bandykite dar kartą.'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Tai jus atjungs nuo šio įrenginio.'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Tai jus atjungs nuo toliau nurodyto įrenginio:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Baigti seansą?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Baigti'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Šis įrenginys'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Kurti paskyrą'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Silpna'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Vidutinė'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stipri'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Ištrinti paskyrą'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Apgailestausime, kad išeinate. Ar susiduriate su kažkokiomis problemomis?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Taip, siųsti atsiliepimą'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Ne, ištrinti paskyrą'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Nustatykite tapatybę, kad pradėtumėte paskyros ištrynimą'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Patvirtinkite paskyros ištrynimą'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Ši paskyra susieta su kitomis „Ente“ programomis, jei jas naudojate.\n\nJūsų įkelti duomenys per visas „Ente“ programas bus planuojama ištrinti, o jūsų paskyra bus ištrinta negrįžtamai.'; @override - String get delete => 'Delete'; + String get delete => 'Ištrinti'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Kurti naują paskyrą'; @override - String get password => 'Password'; + String get password => 'Slaptažodis'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Patvirtinkite slaptažodį'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Slaptažodžio stiprumas: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Kaip išgirdote apie „Ente“? (nebūtina)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Mes nesekame programų diegimų. Mums padėtų, jei pasakytumėte, kur mus radote.'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Sutinku su paslaugų sąlygomis ir privatumo politika'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Sąlygos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privatumo politika'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Suprantu, kad jei prarasiu slaptažodį, galiu prarasti savo duomenis, kadangi duomenys yra visapusiškai užšifruota.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifravimas'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Prisijungti'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Sveiki sugrįžę!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Spustelėjus Prisijungti sutinku su paslaugų sąlygomis ir privatumo politika'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Nėra interneto ryšio'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Patikrinkite savo interneto ryšį ir bandykite dar kartą.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Patvirtinimas nepavyko. Bandykite dar kartą.'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Iš naujo sukurti slaptažodį'; @override String get recreatePasswordBody => - '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).'; + 'Dabartinis įrenginys nėra pakankamai galingas, kad patvirtintų jūsų slaptažodį, bet mes galime iš naujo sugeneruoti taip, kad jis veiktų su visais įrenginiais.\n\nPrisijunkite naudodami atkūrimo raktą ir sugeneruokite iš naujo slaptažodį (jei norite, galite vėl naudoti tą patį).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Naudoti atkūrimo raktą'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Pamiršau slaptažodį'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Keisti el. paštą'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Patvirtinti el. paštą'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Išsiuntėme laišką adresu $email.'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Kad iš naujo nustatytumėte slaptažodį, pirmiausia patvirtinkite savo el. paštą.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Patikrinkite savo gautieją (ir šlamštą), kad užbaigtumėte patvirtinimą.'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Palieskite, kad įvestumėte kodą'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Siųsti el. laišką'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Iš naujo siųsti el. laišką'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Vis dar laukiama patvirtinimo'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Seansas baigėsi'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Jūsų seansas baigėsi. Prisijunkite iš naujo.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Slaptarakčio patvirtinimas'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Laukiama patvirtinimo...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Bandyti dar kartą'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Tikrinti būseną'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Prisijungti su TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Atkurti paskyrą'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nustatyti slaptažodį'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Keisti slaptažodį'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Nustatyti slaptažodį iš naujo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifravimo raktai'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Įveskite slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Įveskite naują slaptažodį, kurį galime naudoti jūsų duomenims užšifruoti'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Šio slaptažodžio nesaugome, todėl jei jį pamiršite, negalėsime iššifruoti jūsų duomenų'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Kaip tai veikia'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generuojami šifravimo raktai...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Slaptažodis sėkmingai pakeistas'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Atsijungti iš kitų įrenginių'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jei manote, kad kas nors gali žinoti jūsų slaptažodį, galite priverstinai atsijungti iš visų kitų įrenginių, naudojančių jūsų paskyrą.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Atsijungti kitus įrenginius'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neatsijungti'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Generuojami šifravimo raktai...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Tęsti'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Nesaugus įrenginys'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Atsiprašome, šiame įrenginyje nepavyko sugeneruoti saugių raktų.\n\nRegistruokitės iš kito įrenginio.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Nukopijuotas atkūrimo raktas į iškarpinę'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Atkūrimo raktas'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jei pamiršote slaptažodį, vienintelis būdas atkurti duomenis – naudoti šį raktą.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Šio rakto nesaugome, todėl išsaugokite šį 24 žodžių raktą saugioje vietoje.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Daryti tai vėliau'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Išsaugoti raktą'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Atkūrimo raktas išsaugotas atsisiuntimų aplanke.'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Neturite atkūrimo rakto?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvigubas tapatybės nustatymas'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Įveskite 6 skaitmenų kodą\niš autentifikatoriaus programos'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Prarastas įrenginys?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Įveskite atkūrimo raktą'; @override - String get recover => 'Recover'; + String get recover => 'Atkurti'; + + @override + String get loggingOut => 'Atsijungiama...'; + + @override + String get immediately => 'Iš karto'; + + @override + String get appLock => 'Programos užraktas'; + + @override + String get autoLock => 'Automatinis užraktas'; + + @override + String get noSystemLockFound => 'Nerastas sistemos užraktas'; + + @override + String get deviceLockEnablePreSteps => + 'Kad įjungtumėte įrenginio užraktą, sistemos nustatymuose nustatykite įrenginio prieigos kodą arba ekrano užraktą.'; + + @override + String get appLockDescription => + 'Pasirinkite tarp numatytojo įrenginio užrakinimo ekrano ir pasirinktinio užrakinimo ekrano su PIN kodu arba slaptažodžiu.'; + + @override + String get deviceLock => 'Įrenginio užraktas'; + + @override + String get pinLock => 'PIN užraktas'; + + @override + String get autoLockFeatureDescription => + 'Laikas, po kurio programa užrakinama perkėlus ją į foną.'; + + @override + String get hideContent => 'Slėpti turinį'; + + @override + String get hideContentDescriptionAndroid => + 'Paslepia programų turinį programų perjungiklyje ir išjungia ekrano kopijas.'; + + @override + String get hideContentDescriptioniOS => + 'Paslepia programos turinį programos perjungiklyje.'; + + @override + String get tooManyIncorrectAttempts => 'Per daug neteisingų bandymų.'; + + @override + String get tapToUnlock => 'Palieskite, kad atrakintumėte'; + + @override + String get areYouSureYouWantToLogout => 'Ar tikrai norite atsijungti?'; + + @override + String get yesLogout => 'Taip, atsijungti'; + + @override + String get authToViewSecrets => + 'Nustatykite tapatybę, kad peržiūrėtumėte savo paslaptis'; + + @override + String get next => 'Toliau'; + + @override + String get setNewPassword => 'Nustatykite naują slaptažodį'; + + @override + String get enterPin => 'Įveskite PIN'; + + @override + String get setNewPin => 'Nustatykite naują PIN'; + + @override + String get confirm => 'Patvirtinti'; + + @override + String get reEnterPassword => 'Įveskite slaptažodį iš naujo'; + + @override + String get reEnterPin => 'Įveskite PIN iš naujo'; + + @override + String get androidBiometricHint => 'Patvirtinkite tapatybę'; + + @override + String get androidBiometricNotRecognized => + 'Neatpažinta. Bandykite dar kartą.'; + + @override + String get androidBiometricSuccess => 'Sėkmė'; + + @override + String get androidCancelButton => 'Atšaukti'; + + @override + String get androidSignInTitle => 'Privalomas tapatybės nustatymas'; + + @override + String get androidBiometricRequiredTitle => 'Privaloma biometrija'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Privalomi įrenginio kredencialai'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Privalomi įrenginio kredencialai'; + + @override + String get goToSettings => 'Eiti į nustatymus'; + + @override + String get androidGoToSettingsDescription => + 'Biometrinis tapatybės nustatymas jūsų įrenginyje nenustatytas. Eikite į Nustatymai > Saugumas ir pridėkite biometrinį tapatybės nustatymą.'; + + @override + String get iOSLockOut => + 'Biometrinis tapatybės nustatymas išjungtas. Kad jį įjungtumėte, užrakinkite ir atrakinkite ekraną.'; + + @override + String get iOSOkButton => 'Gerai'; + + @override + String get emailAlreadyRegistered => 'El. paštas jau užregistruotas.'; + + @override + String get emailNotRegistered => 'El. paštas neregistruotas.'; + + @override + String get thisEmailIsAlreadyInUse => 'Šis el. paštas jau naudojamas.'; + + @override + String emailChangedTo(String newEmail) { + return 'El. paštas pakeistas į $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Tapatybės nustatymas nepavyko. Bandykite dar kartą.'; + + @override + String get authenticationSuccessful => 'Tapatybės nustatymas sėkmingas.'; + + @override + String get sessionExpired => 'Seansas baigėsi'; + + @override + String get incorrectRecoveryKey => 'Neteisingas atkūrimo raktas'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Įvestas atkūrimo raktas yra neteisingas.'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Dvigubas tapatybės nustatymas sėkmingai iš naujo nustatytas.'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Jūsų patvirtinimo kodas nebegaliojantis.'; + + @override + String get incorrectCode => 'Neteisingas kodas'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Atsiprašome, įvestas kodas yra neteisingas.'; + + @override + String get developerSettings => 'Kūrėjo nustatymai'; + + @override + String get serverEndpoint => 'Serverio galutinis taškas'; + + @override + String get invalidEndpoint => 'Netinkamas galutinis taškas'; + + @override + String get invalidEndpointMessage => + 'Atsiprašome, įvestas galutinis taškas netinkamas. Įveskite tinkamą galutinį tašką ir bandykite dar kartą.'; + + @override + String get endpointUpdatedMessage => 'Galutinis taškas sėkmingai atnaujintas'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart index 1ec97207d9..7fb99652a5 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_nl.dart @@ -103,333 +103,530 @@ class StringsLocalizationsNl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Wil je dit opslaan naar je opslagruimte (Downloads map)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Voer uw nieuwe e-mailadres in'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Verifiëren'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ongeldig e-mailadres'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Voer een geldig e-mailadres in.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Een ogenblik geduld...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bevestig wachtwoord'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Onjuist wachtwoord'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Probeer het nog eens'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Voer wachtwoord in'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Voer je wachtwoord in'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Actieve sessies'; @override - String get oops => 'Oops'; + String get oops => 'Oeps'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Er is iets fout gegaan, probeer het opnieuw'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Dit zal je uitloggen van dit apparaat!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Dit zal je uitloggen van het volgende apparaat:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Sessie beëindigen?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Beëindigen'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Dit apparaat'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Account aanmaken'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Zwak'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Matig'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Sterk'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Account verwijderen'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'We zullen het vervelend vinden om je te zien vertrekken. Zijn er problemen?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, geef feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nee, verwijder account'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Gelieve te verifiëren om het account te verwijderen'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Account verwijderen bevestigen'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Dit account is gekoppeld aan andere Ente apps, als je er gebruik van maakt.\n\nJe geüploade gegevens worden in alle Ente apps gepland voor verwijdering, en je account wordt permanent verwijderd voor alle Ente diensten.'; @override - String get delete => 'Delete'; + String get delete => 'Verwijderen'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Nieuw account aanmaken'; @override - String get password => 'Password'; + String get password => 'Wachtwoord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Wachtwoord bevestigen'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Wachtwoord sterkte: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hoe hoorde je over Ente? (optioneel)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Wij gebruiken geen tracking. Het zou helpen als je ons vertelt waar je ons gevonden hebt!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Ik ga akkoord met de gebruiksvoorwaarden en privacybeleid'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Voorwaarden'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privacybeleid'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Ik begrijp dat als ik mijn wachtwoord verlies, ik mijn gegevens kan verliezen omdat mijn gegevens end-to-end versleuteld zijn.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Encryptie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Inloggen'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Welkom terug!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Door op inloggen te klikken, ga ik akkoord met de gebruiksvoorwaarden en privacybeleid'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Geen internetverbinding'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Controleer je internetverbinding en probeer het opnieuw.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verificatie mislukt, probeer het opnieuw'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Wachtwoord opnieuw instellen'; @override String get recreatePasswordBody => - '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).'; + '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 herstelsleutel en genereer opnieuw uw wachtwoord (je kunt dezelfde indien gewenst opnieuw gebruiken).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Herstelsleutel gebruiken'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Wachtwoord vergeten'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'E-mailadres wijzigen'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Bevestig e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'We hebben een e-mail gestuurd naar $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Verifieer eerst je e-mailadres om je wachtwoord opnieuw in te stellen.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Controleer je inbox (en spam) om verificatie te voltooien'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tik om code in te voeren'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'E-mail versturen'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'E-mail opnieuw versturen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verificatie is nog in behandeling'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessie verlopen'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Jouw sessie is verlopen. Log opnieuw in.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Passkey verificatie'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Wachten op verificatie...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Probeer opnieuw'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Status controleren'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Inloggen met TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Account herstellen'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Wachtwoord instellen'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Wachtwoord wijzigen'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Wachtwoord resetten'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Encryptiesleutels'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Voer een wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Voer een nieuw wachtwoord in dat we kunnen gebruiken om je gegevens te versleutelen'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Wij slaan dit wachtwoord niet op, dus als je het vergeet, kunnen we jouw gegevens niet ontsleutelen'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Hoe het werkt'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Encryptiesleutels worden gegenereerd...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Wachtwoord succesvol aangepast'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Afmelden bij andere apparaten'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Als je denkt dat iemand je wachtwoord zou kunnen kennen, kun je alle andere apparaten die je account gebruiken dwingen om uit te loggen.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Afmelden bij andere apparaten'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Niet uitloggen'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Encryptiesleutels genereren...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Doorgaan'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Onveilig apparaat'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Sorry, we konden geen beveiligde sleutels genereren op dit apparaat.\n\nMeld je aan vanaf een ander apparaat.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Herstelsleutel gekopieerd naar klembord'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Herstelsleutel'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Als je je wachtwoord vergeet, kun je alleen met deze code je gegevens herstellen.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'We slaan deze code niet op, bewaar deze code met 24 woorden op een veilige plaats.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Doe dit later'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Sleutel opslaan'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Herstelsleutel opgeslagen in de Downloads map!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Geen herstelsleutel?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tweestapsverificatie'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Voer de 6-cijferige code van je verificatie-app in'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Apparaat verloren?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Voer je herstelsleutel in'; @override - String get recover => 'Recover'; + String get recover => 'Herstellen'; + + @override + String get loggingOut => 'Bezig met uitloggen...'; + + @override + String get immediately => 'Onmiddellijk'; + + @override + String get appLock => 'App-vergrendeling'; + + @override + String get autoLock => 'Automatische vergrendeling'; + + @override + String get noSystemLockFound => 'Geen systeemvergrendeling gevonden'; + + @override + String get deviceLockEnablePreSteps => + 'Om toestelvergrendeling in te schakelen, stelt u de toegangscode van het apparaat of schermvergrendeling in uw systeeminstellingen in.'; + + @override + String get appLockDescription => + 'Kies tussen de standaard schermvergrendeling van uw apparaat en een aangepaste schermvergrendeling met een pincode of wachtwoord.'; + + @override + String get deviceLock => 'Apparaat vergrendeling'; + + @override + String get pinLock => 'Pin vergrendeling'; + + @override + String get autoLockFeatureDescription => + 'Tijd waarna de app vergrendelt nadat ze op de achtergrond is gezet'; + + @override + String get hideContent => 'Inhoud verbergen'; + + @override + String get hideContentDescriptionAndroid => + 'Verbergt de app inhoud in de app switcher en schakelt schermafbeeldingen uit'; + + @override + String get hideContentDescriptioniOS => + 'Verbergt de inhoud van de app in de app switcher'; + + @override + String get tooManyIncorrectAttempts => 'Te veel onjuiste pogingen'; + + @override + String get tapToUnlock => 'Tik om te ontgrendelen'; + + @override + String get areYouSureYouWantToLogout => + 'Weet je zeker dat je wilt uitloggen?'; + + @override + String get yesLogout => 'Ja, uitloggen'; + + @override + String get authToViewSecrets => + 'Graag verifiëren om uw herstelsleutel te bekijken'; + + @override + String get next => 'Volgende'; + + @override + String get setNewPassword => 'Nieuw wachtwoord instellen'; + + @override + String get enterPin => 'Pin invoeren'; + + @override + String get setNewPin => 'Nieuwe pin instellen'; + + @override + String get confirm => 'Bevestig'; + + @override + String get reEnterPassword => 'Wachtwoord opnieuw invoeren'; + + @override + String get reEnterPin => 'PIN opnieuw invoeren'; + + @override + String get androidBiometricHint => 'Identiteit verifiëren'; + + @override + String get androidBiometricNotRecognized => + 'Niet herkend. Probeer het opnieuw.'; + + @override + String get androidBiometricSuccess => 'Succes'; + + @override + String get androidCancelButton => 'Annuleren'; + + @override + String get androidSignInTitle => 'Verificatie vereist'; + + @override + String get androidBiometricRequiredTitle => + 'Biometrische verificatie vereist'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Apparaatgegevens vereist'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Apparaatgegevens vereist'; + + @override + String get goToSettings => 'Ga naar instellingen'; + + @override + String get androidGoToSettingsDescription => + 'Biometrische verificatie is niet ingesteld op uw apparaat. Ga naar \'Instellingen > Beveiliging\' om biometrische verificatie toe te voegen.'; + + @override + String get iOSLockOut => + 'Biometrische verificatie is uitgeschakeld. Vergrendel en ontgrendel uw scherm om het in te schakelen.'; + + @override + String get iOSOkButton => 'Oké'; + + @override + String get emailAlreadyRegistered => 'E-mail is al geregistreerd.'; + + @override + String get emailNotRegistered => 'E-mail niet geregistreerd.'; + + @override + String get thisEmailIsAlreadyInUse => 'Dit e-mailadres is al in gebruik'; + + @override + String emailChangedTo(String newEmail) { + return 'E-mailadres gewijzigd naar $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Verificatie mislukt, probeer het opnieuw'; + + @override + String get authenticationSuccessful => 'Verificatie geslaagd!'; + + @override + String get sessionExpired => 'Sessie verlopen'; + + @override + String get incorrectRecoveryKey => 'Onjuiste herstelsleutel'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'De ingevoerde herstelsleutel is onjuist'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Tweestapsverificatie succesvol gereset'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'Uw verificatiecode is verlopen'; + + @override + String get incorrectCode => 'Onjuiste code'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Sorry, de ingevoerde code is onjuist'; + + @override + String get developerSettings => 'Ontwikkelaarsinstellingen'; + + @override + String get serverEndpoint => 'Server eindpunt'; + + @override + String get invalidEndpoint => 'Ongeldig eindpunt'; + + @override + String get invalidEndpointMessage => + 'Sorry, het eindpunt dat u hebt ingevoerd is ongeldig. Voer een geldig eindpunt in en probeer het opnieuw.'; + + @override + String get endpointUpdatedMessage => 'Eindpunt met succes bijgewerkt'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart index adf63c13c7..5d636efe39 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pl.dart @@ -103,333 +103,528 @@ class StringsLocalizationsPl extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Czy chcesz zapisać to do swojej pamięci masowej (domyślnie folder Pobrane)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Wprowadź nowy adres e-mail'; @override String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Zweryfikuj'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Nieprawidłowy adres e-mail'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Prosimy podać prawidłowy adres e-mail.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Prosimy czekać...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Zweryfikuj hasło'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nieprawidłowe hasło'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Prosimy spróbować ponownie'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Wprowadź hasło'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Wprowadź swoje hasło'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktywne sesje'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Coś poszło nie tak, spróbuj ponownie'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'To wyloguje Cię z tego urządzenia!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'To wyloguje Cię z tego urządzenia:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Zakończyć sesję?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Zakończ'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'To urządzenie'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Utwórz konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Słabe'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Umiarkowane'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silne'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Usuń konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Będzie nam przykro, że odchodzisz. Masz jakiś problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Tak, wyślij opinię'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nie, usuń moje konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Prosimy uwierzytelnić się, aby zainicjować usuwanie konta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potwierdź usunięcie konta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'To konto jest połączone z innymi aplikacjami Ente, jeśli ich używasz.\n\nTwoje przesłane dane, we wszystkich aplikacjach Ente, zostaną zaplanowane do usunięcia, a Twoje konto zostanie trwale usunięte.'; @override - String get delete => 'Delete'; + String get delete => 'Usuń'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Utwórz nowe konto'; @override - String get password => 'Password'; + String get password => 'Hasło'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potwierdź hasło'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Siła hasła: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Jak usłyszałeś/aś o Ente? (opcjonalnie)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nie śledzimy instalacji aplikacji. Pomogłyby nam, gdybyś powiedział/a nam, gdzie nas znalazłeś/aś!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Akceptuję warunki korzystania z usługi i politykę prywatności'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Regulamin'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Polityka prywatności'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumiem, że jeśli utracę hasło, mogę stracić moje dane, ponieważ moje dane są szyfrowane end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Szyfrowanie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Zaloguj się'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Witaj ponownie!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Klikając, zaloguj się, zgadzam się na regulamin i politykę prywatności'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Brak połączenia z Internetem'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Prosimy sprawdzić połączenie internetowe i spróbować ponownie.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Weryfikacja nie powiodła się, spróbuj ponownie'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Zresetuj hasło'; @override String get recreatePasswordBody => - '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).'; + 'Obecne urządzenie nie jest wystarczająco wydajne, aby zweryfikować Twoje hasło, więc musimy je raz zregenerować w sposób, który działa ze wszystkimi urządzeniami. \n\nZaloguj się przy użyciu klucza odzyskiwania i zresetuj swoje hasło (możesz ponownie użyć tego samego, jeśli chcesz).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Użyj kodu odzyskiwania'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Nie pamiętam hasła'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Zmień adres e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Zweryfikuj adres e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Wysłaliśmy wiadomość do $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Aby zresetować hasło, najpierw zweryfikuj swój e-mail.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Sprawdź swoją skrzynkę odbiorczą (i spam), aby zakończyć weryfikację'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Dotknij, aby wprowadzić kod'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Wyślij e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Wyślij e-mail ponownie'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Weryfikacja jest nadal w toku'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sesja wygasła'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Twoja sesja wygasła. Zaloguj się ponownie.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Weryfikacja kluczem dostępu'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Oczekiwanie na weryfikację...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Spróbuj ponownie'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Sprawdź stan'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Zaloguj się za pomocą TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Odzyskaj konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ustaw hasło'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Zmień hasło'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Zresetuj hasło'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Klucz szyfrowania'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Wprowadź hasło, którego możemy użyć do zaszyfrowania Twoich danych'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Wprowadź nowe hasło, którego możemy użyć do zaszyfrowania Twoich danych'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Nie przechowujemy tego hasła, więc jeśli go zapomnisz, nie będziemy w stanie odszyfrować Twoich danych'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Jak to działa'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generowanie kluczy szyfrujących...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Hasło zostało pomyślnie zmienione'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Wyloguj z pozostałych urządzeń'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Jeśli uważasz, że ktoś może znać Twoje hasło, możesz wymusić wylogowanie na wszystkich innych urządzeniach korzystających z Twojego konta.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Wyloguj z pozostałych urządzeń'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Nie wylogowuj mnie'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Generowanie kluczy szyfrujących...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Kontynuuj'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Niezabezpieczone urządzenie'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Przepraszamy, nie mogliśmy wygenerować kluczy bezpiecznych na tym urządzeniu.\n\nZarejestruj się z innego urządzenia.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Klucz odzyskiwania został skopiowany do schowka'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Klucz odzyskiwania'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Jeśli zapomnisz hasła, jedynym sposobem na odzyskanie danych jest ten klucz.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Nie przechowujemy tego klucza, prosimy zachować ten 24-słowny klucz w bezpiecznym miejscu.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Zrób to później'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Zapisz klucz'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Klucz odzyskiwania zapisany w folderze Pobrane!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Brak klucza odzyskiwania?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Uwierzytelnianie dwustopniowe'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Wprowadź sześciocyfrowy kod z \nTwojej aplikacji uwierzytelniającej'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Zagubiono urządzenie?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Wprowadź swój klucz odzyskiwania'; @override - String get recover => 'Recover'; + String get recover => 'Odzyskaj'; + + @override + String get loggingOut => 'Wylogowywanie...'; + + @override + String get immediately => 'Natychmiast'; + + @override + String get appLock => 'Blokada aplikacji'; + + @override + String get autoLock => 'Automatyczna blokada'; + + @override + String get noSystemLockFound => 'Nie znaleziono blokady systemowej'; + + @override + String get deviceLockEnablePreSteps => + 'Aby włączyć blokadę aplikacji, należy skonfigurować hasło urządzenia lub blokadę ekranu w ustawieniach Twojego systemu.'; + + @override + String get appLockDescription => + 'Wybierz między domyślnym ekranem blokady urządzenia a niestandardowym ekranem blokady z kodem PIN lub hasłem.'; + + @override + String get deviceLock => 'Blokada urządzenia'; + + @override + String get pinLock => 'Blokada PIN'; + + @override + String get autoLockFeatureDescription => + 'Czas, po którym aplikacja blokuje się po umieszczeniu jej w tle'; + + @override + String get hideContent => 'Ukryj zawartość'; + + @override + String get hideContentDescriptionAndroid => + 'Ukrywa zawartość aplikacji w przełączniku aplikacji i wyłącza zrzuty ekranu'; + + @override + String get hideContentDescriptioniOS => + 'Ukrywa zawartość aplikacji w przełączniku aplikacji'; + + @override + String get tooManyIncorrectAttempts => 'Zbyt wiele błędnych prób'; + + @override + String get tapToUnlock => 'Naciśnij, aby odblokować'; + + @override + String get areYouSureYouWantToLogout => 'Czy na pewno chcesz się wylogować?'; + + @override + String get yesLogout => 'Tak, wyloguj'; + + @override + String get authToViewSecrets => + 'Prosimy uwierzytelnić się, aby wyświetlić swoje sekrety'; + + @override + String get next => 'Dalej'; + + @override + String get setNewPassword => 'Ustaw nowe hasło'; + + @override + String get enterPin => 'Wprowadź kod PIN'; + + @override + String get setNewPin => 'Ustaw nowy kod PIN'; + + @override + String get confirm => 'Potwierdź'; + + @override + String get reEnterPassword => 'Wprowadź ponownie hasło'; + + @override + String get reEnterPin => 'Wprowadź ponownie kod PIN'; + + @override + String get androidBiometricHint => 'Potwierdź swoją tożsamość'; + + @override + String get androidBiometricNotRecognized => + 'Nie rozpoznano. Spróbuj ponownie.'; + + @override + String get androidBiometricSuccess => 'Sukces'; + + @override + String get androidCancelButton => 'Anuluj'; + + @override + String get androidSignInTitle => 'Wymagana autoryzacja'; + + @override + String get androidBiometricRequiredTitle => 'Wymagana biometria'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Wymagane dane logowania urządzenia'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Wymagane dane logowania urządzenia'; + + @override + String get goToSettings => 'Przejdź do ustawień'; + + @override + String get androidGoToSettingsDescription => + 'Uwierzytelnianie biometryczne nie jest skonfigurowane na tym urządzeniu. Przejdź do \'Ustawienia > Bezpieczeństwo\', aby dodać uwierzytelnianie biometryczne.'; + + @override + String get iOSLockOut => + 'Uwierzytelnianie biometryczne jest wyłączone. Prosimy zablokować i odblokować ekran, aby je włączyć.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Adres e-mail jest już zarejestrowany.'; + + @override + String get emailNotRegistered => 'Adres e-mail nie jest zarejestrowany.'; + + @override + String get thisEmailIsAlreadyInUse => 'Ten adres e-mail już jest zajęty'; + + @override + String emailChangedTo(String newEmail) { + return 'Adres e-mail został zmieniony na $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Uwierzytelnianie nie powiodło się, prosimy spróbować ponownie'; + + @override + String get authenticationSuccessful => 'Uwierzytelnianie powiodło się!'; + + @override + String get sessionExpired => 'Sesja wygasła'; + + @override + String get incorrectRecoveryKey => 'Nieprawidłowy klucz odzyskiwania'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Wprowadzony klucz odzyskiwania jest nieprawidłowy'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Pomyślnie zresetowano uwierzytelnianie dwustopniowe'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'Twój kod weryfikacyjny wygasł'; + + @override + String get incorrectCode => 'Nieprawidłowy kod'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Niestety, wprowadzony kod jest nieprawidłowy'; + + @override + String get developerSettings => 'Ustawienia dla programistów'; + + @override + String get serverEndpoint => 'Punkt końcowy serwera'; + + @override + String get invalidEndpoint => 'Punkt końcowy jest nieprawidłowy'; + + @override + String get invalidEndpointMessage => + 'Niestety, wprowadzony punkt końcowy jest nieprawidłowy. Wprowadź prawidłowy punkt końcowy i spróbuj ponownie.'; + + @override + String get endpointUpdatedMessage => 'Punkt końcowy zaktualizowany pomyślnie'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart index 13074ce5e8..b339c4f481 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_pt.dart @@ -103,333 +103,527 @@ class StringsLocalizationsPt extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Deseja mesmo salvar em seu armazenamento (pasta de Downloads por padrão)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Insira seu novo e-mail'; @override - String get email => 'Email'; + String get email => 'E-mail'; @override - String get verify => 'Verify'; + String get verify => 'Verificar'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Endereço de e-mail inválido'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Insira um endereço de e-mail válido.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Aguarde...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Verificar senha'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Senha incorreta'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Tente novamente'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Inserir senha'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Insira sua senha'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Sessões ativas'; @override - String get oops => 'Oops'; + String get oops => 'Opa'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Algo deu errado. Tente outra vez'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Isso fará com que você saia deste dispositivo!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Isso fará você sair do dispositivo a seguir:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Sair?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Encerrar'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Esse dispositivo'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Criar conta'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Fraca'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Moderada'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Forte'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Excluir conta'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Estamos tristes por vê-lo sair. Você enfrentou algum problema?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Sim, enviar feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Não, excluir conta'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Autentique-se para iniciar a exclusão de conta'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Confirmar exclusão de conta'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Esta conta está vinculada a outros apps Ente, se você usa algum.\n\nSeus dados enviados, entre todos os apps Ente, serão marcados para exclusão, e sua conta será apagada permanentemente.'; @override - String get delete => 'Delete'; + String get delete => 'Excluir'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Criar nova conta'; @override - String get password => 'Password'; + String get password => 'Senha'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Confirmar senha'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Força da senha: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Como você descobriu o Ente? (opcional)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Não rastreamos instalações. Ajudaria bastante se você contasse onde nos achou!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Eu concordo com os termos de serviço e a política de privacidade'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Termos'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Política de Privacidade'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Eu entendo que se eu perder minha senha, posso perder meus dados, já que meus dados são criptografados de ponta a ponta.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Criptografia'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Entrar'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Bem-vindo(a) de volta!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Ao clicar em iniciar sessão, eu concordo com os termos de serviço e a política de privacidade'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Não conectado à internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Verifique sua conexão com a internet e tente novamente.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Falhou na verificação. Tente novamente'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Redefinir senha'; @override String get recreatePasswordBody => - '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).'; + 'Não é possível verificar a sua senha no dispositivo atual, mas podemos regenerá-la para que funcione em todos os dispositivos. \n\nEntre com a sua chave de recuperação e regenere sua senha (você pode usar a mesma se quiser).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Usar chave de recuperação'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Esqueci a senha'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Alterar e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verificar e-mail'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Enviamos um e-mail à $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Para redefinir sua senha, verifique seu e-mail primeiramente.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Verifique sua caixa de entrada (e spam) para concluir a verificação'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Toque para inserir código'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Enviar e-mail'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Reenviar e-mail'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'A verificação ainda está pendente'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessão expirada'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Sua sessão expirou. Registre-se novamente.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verificação de chave de acesso'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Aguardando verificação...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Tente novamente'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Verificar status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Registrar com TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Recuperar conta'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Definir senha'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Alterar senha'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Redefinir senha'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Chaves de criptografia'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Insira uma senha que podemos usar para criptografar seus dados'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Insira uma nova senha para criptografar seus dados'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Não salvamos esta senha, então se você esquecê-la, não podemos descriptografar seus dados'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Como funciona'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Gerando chaves de criptografia...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'A senha foi alterada'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Sair da conta em outros dispositivos'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Se você acha que alguém possa saber da sua senha, você pode forçar desconectar sua conta de outros dispositivos.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Sair em outros dispositivos'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Não sair'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Gerando chaves de criptografia...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Continuar'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Dispositivo inseguro'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Desculpe, não foi possível gerar chaves de segurança nesse dispositivo.\n\ninicie sessão em um dispositivo diferente.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Chave de recuperação copiada para a área de transferência'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Chave de recuperação'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Caso esqueça sua senha, a única maneira de recuperar seus dados é com esta chave.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Não armazenamos esta chave de 24 palavras. Salve-a em um lugar seguro.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Fazer isso depois'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Salvar chave'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Chave de recuperação salva na pasta Downloads!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Sem chave de recuperação?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Autenticação de dois fatores'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Insira o código de 6 dígitos do aplicativo autenticador'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Perdeu o dispositivo?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Digite a chave de recuperação'; @override - String get recover => 'Recover'; + String get recover => 'Recuperar'; + + @override + String get loggingOut => 'Desconectando...'; + + @override + String get immediately => 'Imediatamente'; + + @override + String get appLock => 'Bloqueio do aplicativo'; + + @override + String get autoLock => 'Bloqueio automático'; + + @override + String get noSystemLockFound => 'Nenhum bloqueio do sistema encontrado'; + + @override + String get deviceLockEnablePreSteps => + 'Para ativar o bloqueio do dispositivo, configure a senha do dispositivo ou o bloqueio de tela nas configurações do seu sistema.'; + + @override + String get appLockDescription => + 'Escolha entre a tela de bloqueio padrão do seu dispositivo e uma tela de bloqueio personalizada com PIN ou senha.'; + + @override + String get deviceLock => 'Bloqueio do dispositivo'; + + @override + String get pinLock => 'PIN de bloqueio'; + + @override + String get autoLockFeatureDescription => + 'Tempo de bloqueio do aplicativo em segundo plano'; + + @override + String get hideContent => 'Ocultar conteúdo'; + + @override + String get hideContentDescriptionAndroid => + 'Oculta o conteúdo do aplicativo no seletor de aplicativos e desativa as capturas de tela'; + + @override + String get hideContentDescriptioniOS => + 'Oculta o conteúdo do seletor de aplicativos'; + + @override + String get tooManyIncorrectAttempts => 'Muitas tentativas incorretas'; + + @override + String get tapToUnlock => 'Toque para desbloquear'; + + @override + String get areYouSureYouWantToLogout => 'Deseja mesmo sair?'; + + @override + String get yesLogout => 'Sim, quero sair'; + + @override + String get authToViewSecrets => 'Autentique-se para ver suas chaves secretas'; + + @override + String get next => 'Avançar'; + + @override + String get setNewPassword => 'Defina a nova senha'; + + @override + String get enterPin => 'Inserir PIN'; + + @override + String get setNewPin => 'Definir novo PIN'; + + @override + String get confirm => 'Confirmar'; + + @override + String get reEnterPassword => 'Reinserir senha'; + + @override + String get reEnterPin => 'Reinserir PIN'; + + @override + String get androidBiometricHint => 'Verificar identidade'; + + @override + String get androidBiometricNotRecognized => 'Não reconhecido. Tente de novo.'; + + @override + String get androidBiometricSuccess => 'Sucesso'; + + @override + String get androidCancelButton => 'Cancelar'; + + @override + String get androidSignInTitle => 'Autenticação necessária'; + + @override + String get androidBiometricRequiredTitle => 'Biometria necessária'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Credenciais necessários do dispositivo'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Credenciais necessários do dispositivo'; + + @override + String get goToSettings => 'Ir para Opções'; + + @override + String get androidGoToSettingsDescription => + 'A autenticação biométrica não está configurada no seu dispositivo. Vá em \'Configurações > Segurança\' para adicionar a autenticação biométrica.'; + + @override + String get iOSLockOut => + 'A autenticação biométrica está desativada. Bloqueie e desbloqueie sua tela para ativá-la.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'E-mail já registrado.'; + + @override + String get emailNotRegistered => 'E-mail não registrado.'; + + @override + String get thisEmailIsAlreadyInUse => 'Este e-mail já está em uso'; + + @override + String emailChangedTo(String newEmail) { + return 'E-mail alterado para $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'A autenticação falhou. Tente novamente'; + + @override + String get authenticationSuccessful => 'Autenticado!'; + + @override + String get sessionExpired => 'Sessão expirada'; + + @override + String get incorrectRecoveryKey => 'Chave de recuperação incorreta'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'A chave de recuperação inserida está incorreta'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Autenticação de dois fatores redefinida com sucesso'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Seu código de verificação expirou'; + + @override + String get incorrectCode => 'Código incorreto'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'O código inserido está incorreto'; + + @override + String get developerSettings => 'Opções de Desenvolvedor'; + + @override + String get serverEndpoint => 'Endpoint do servidor'; + + @override + String get invalidEndpoint => 'Endpoint inválido'; + + @override + String get invalidEndpointMessage => + 'Desculpe, o ponto de acesso inserido é inválido. Insira um ponto de acesso válido e tente novamente.'; + + @override + String get endpointUpdatedMessage => 'O endpoint foi atualizado'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart index 3f48688420..52dfd4e049 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_ru.dart @@ -103,333 +103,530 @@ class StringsLocalizationsRu extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Вы хотите сохранить это в хранилище (по умолчанию папка загрузок)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Введите ваш новый адрес электронной почты'; @override - String get email => 'Email'; + String get email => 'Электронная почта'; @override - String get verify => 'Verify'; + String get verify => 'Подтвердить'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Неверный адрес электронной почты'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Пожалуйста, введите действительный адрес электронной почты.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Пожалуйста, подождите...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Подтверждение пароля'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Неправильный пароль'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Пожалуйста, попробуйте ещё раз'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Введите пароль'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Введите пароль'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активные сеансы'; @override - String get oops => 'Oops'; + String get oops => 'Ой'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Что-то пошло не так. Попробуйте еще раз'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => 'Вы выйдете из этого устройства!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Вы выйдете из списка следующих устройств:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Завершить сеанс?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Завершить'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Это устройство'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Создать аккаунт'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слабый'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Средний'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Сильный'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Удалить аккаунт'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Нам будет жаль, если вы уйдете. Вы столкнулись с какой-то проблемой?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, отправить отзыв'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Нет, удалить аккаунт'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Пожалуйста, авторизуйтесь, чтобы начать удаление аккаунта'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Подтвердить удаление аккаунта'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Эта учетная запись связана с другими приложениями Ente, если вы ими пользуетесь.\n\nЗагруженные вами данные во всех приложениях Ente будут запланированы к удалению, а ваша учетная запись будет удалена без возможности восстановления.'; @override - String get delete => 'Delete'; + String get delete => 'Удалить'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Создать новый аккаунт'; @override - String get password => 'Password'; + String get password => 'Пароль'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Подтвердить пароль'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Мощность пароля: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Как вы узнали о Ente? (необязательно)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Мы не отслеживаем установки приложений. Было бы полезно, если бы вы сказали, где нас нашли!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Я согласен с условиями предоставления услуг и политикой конфиденциальности'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Условия использования'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика конфиденциальности'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Я понимаю, что если я потеряю свой пароль, я могу потерять свои данные, так как мои данные в сквозном шифровании.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифрование'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Войти'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'С возвращением!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Нажимая на логин, я принимаю условия использования и политику конфиденциальности'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Нет подключения к Интернету'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Проверьте подключение к Интернету и повторите попытку.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Проверка не удалась, попробуйте еще раз'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Пересоздать пароль'; @override String get recreatePasswordBody => - '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).'; + 'Текущее устройство недостаточно мощно для верификации пароля, но мы можем регенерировать так, как это работает со всеми устройствами.\n\nПожалуйста, войдите, используя ваш ключ восстановления и сгенерируйте ваш пароль (вы можете использовать тот же пароль, если пожелаете).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Использовать ключ восстановления'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Забыл пароль'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Изменить адрес электронной почты'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Подтвердить адрес электронной почты'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Мы отправили письмо на $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Подтвердите адрес электронной почты, чтобы сбросить пароль.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Пожалуйста, проверьте свой почтовый ящик (и спам) для завершения верификации'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Нажмите, чтобы ввести код'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Отправить электронное письмо'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Отправить письмо еще раз'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Верификация еще не завершена'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сессия недействительна'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => 'Сессия истекла. Войдите снова.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Проверка с помощью ключа доступа'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Ожидание подтверждения...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Попробовать снова'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Проверить статус'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Войти с помощью TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Восстановить аккаунт'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Поставить пароль'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Изменить пароль'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Сбросить пароль'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Ключи шифрования'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Введите пароль, который мы можем использовать для шифрования ваших данных'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Введите новый пароль, который мы можем использовать для шифрования ваших данных'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Мы не храним этот пароль, поэтому если вы забудете его, мы не сможем расшифровать ваши данные'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Как это работает'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Генерируем ключи шифрования...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Пароль успешно изменён'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Выйти из других устройств'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Если вы думаете, что кто-то может знать ваш пароль, вы можете принудительно выйти из всех устройств.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Выйти из других устройств'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не выходить'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Генерируем ключи шифрования...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Далее'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Небезопасное устройство'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'К сожалению, мы не смогли сгенерировать безопасные ключи на этом устройстве.\n\nПожалуйста, зарегистрируйтесь с другого устройства.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Ключ восстановления скопирован в буфер обмена'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Ключ восстановления'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Если вы забыли свой пароль, то восстановить данные можно только с помощью этого ключа.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Мы не храним этот ключ, пожалуйста, сохраните этот ключ в безопасном месте.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Сделать позже'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Сохранить ключ'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Ключ восстановления сохранён в папке Загрузки!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Нет ключа восстановления?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Двухфакторная аутентификация'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Введите 6-значный код из\nвашего приложения-аутентификатора'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Потеряно устройство?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Введите ключ восстановления'; @override - String get recover => 'Recover'; + String get recover => 'Восстановить'; + + @override + String get loggingOut => 'Выходим...'; + + @override + String get immediately => 'Немедленно'; + + @override + String get appLock => 'Блокировка приложения'; + + @override + String get autoLock => 'Автоблокировка'; + + @override + String get noSystemLockFound => 'Системная блокировка не найдена'; + + @override + String get deviceLockEnablePreSteps => + 'Чтобы включить блокировку устройства, пожалуйста, настройте пароль или блокировку экрана в настройках системы.'; + + @override + String get appLockDescription => + 'Выберите между экраном блокировки вашего устройства и пользовательским экраном блокировки с PIN-кодом или паролем.'; + + @override + String get deviceLock => 'Блокировка устройства'; + + @override + String get pinLock => 'Pin блокировка'; + + @override + String get autoLockFeatureDescription => + 'Время в фоне, после которого приложение блокируется'; + + @override + String get hideContent => 'Скрыть содержимое'; + + @override + String get hideContentDescriptionAndroid => + 'Скрывает содержимое приложения в переключателе приложений и отключает скриншоты'; + + @override + String get hideContentDescriptioniOS => + 'Скрывает содержимое приложения в переключателе приложений'; + + @override + String get tooManyIncorrectAttempts => 'Слишком много неудачных попыток'; + + @override + String get tapToUnlock => 'Нажмите для разблокировки'; + + @override + String get areYouSureYouWantToLogout => 'Вы уверены, что хотите выйти?'; + + @override + String get yesLogout => 'Да, выйти'; + + @override + String get authToViewSecrets => + 'Пожалуйста, авторизуйтесь для просмотра ваших секретов'; + + @override + String get next => 'Далее'; + + @override + String get setNewPassword => 'Задать новый пароль'; + + @override + String get enterPin => 'Введите PIN'; + + @override + String get setNewPin => 'Установите новый PIN'; + + @override + String get confirm => 'Подтвердить'; + + @override + String get reEnterPassword => 'Подтвердите пароль'; + + @override + String get reEnterPin => 'Введите PIN-код ещё раз'; + + @override + String get androidBiometricHint => 'Подтвердите личность'; + + @override + String get androidBiometricNotRecognized => + 'Не распознано. Попробуйте еще раз.'; + + @override + String get androidBiometricSuccess => 'Успешно'; + + @override + String get androidCancelButton => 'Отменить'; + + @override + String get androidSignInTitle => 'Требуется аутентификация'; + + @override + String get androidBiometricRequiredTitle => 'Требуется биометрия'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Требуются учетные данные устройства'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Требуются учетные данные устройства'; + + @override + String get goToSettings => 'Перейдите к настройкам'; + + @override + String get androidGoToSettingsDescription => + 'Биометрическая аутентификация не настроена на вашем устройстве. Перейдите в \"Настройки > Безопасность\", чтобы добавить биометрическую аутентификацию.'; + + @override + String get iOSLockOut => + 'Биометрическая аутентификация отключена. Пожалуйста, заблокируйте и разблокируйте экран, чтобы включить ее.'; + + @override + String get iOSOkButton => 'ОК'; + + @override + String get emailAlreadyRegistered => + 'Адрес электронной почты уже зарегистрирован.'; + + @override + String get emailNotRegistered => + 'Адрес электронной почты не зарегистрирован.'; + + @override + String get thisEmailIsAlreadyInUse => + 'Этот адрес электронной почты уже используется'; + + @override + String emailChangedTo(String newEmail) { + return 'Адрес электронной почты изменен на $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Аутентификация не удалась, попробуйте еще раз'; + + @override + String get authenticationSuccessful => 'Аутентификация прошла успешно!'; + + @override + String get sessionExpired => 'Сеанс истек'; + + @override + String get incorrectRecoveryKey => 'Неправильный ключ восстановления'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Введен неправильный ключ восстановления'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Двухфакторная аутентификация успешно сброшена'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Срок действия вашего проверочного кода истек'; + + @override + String get incorrectCode => 'Неверный код'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Извините, введенный вами код неверный'; + + @override + String get developerSettings => 'Настройки разработчика'; + + @override + String get serverEndpoint => 'Конечная точка сервера'; + + @override + String get invalidEndpoint => 'Неверная конечная точка'; + + @override + String get invalidEndpointMessage => + 'Извините, введенная вами конечная точка неверна. Пожалуйста, введите корректную конечную точку и повторите попытку.'; + + @override + String get endpointUpdatedMessage => 'Конечная точка успешно обновлена'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart index 8c96113796..6ab32314df 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sk.dart @@ -103,7 +103,7 @@ class StringsLocalizationsSk extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Chcete to uložiť do svojho zariadenia (predvolený priečinok Stiahnuté súbory)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @@ -112,324 +112,518 @@ class StringsLocalizationsSk extends StringsLocalizations { String get email => 'Email'; @override - String get verify => 'Verify'; + String get verify => 'Overiť'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Neplatná emailová adresa'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Zadajte platnú e-mailovú adresu.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Prosím počkajte...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Potvrďte heslo'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Nesprávne heslo'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Prosím, skúste to znova'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Zadajte heslo'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Zadajte vaše heslo'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktívne relácie'; @override - String get oops => 'Oops'; + String get oops => 'Ups'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Niečo sa pokazilo, skúste to prosím znova'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Toto vás odhlási z tohto zariadenia!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Toto vás odhlási z následujúceho zariadenia:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Ukončiť reláciu?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Ukončiť'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Toto zariadenie'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Vytvoriť účet'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Slabé'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Mierne'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Silné'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Odstrániť účet'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Bude nám ľúto ak odídeš. Máš nejaký problém?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Áno, odoslať spätnú väzbu'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nie, odstrániť účet'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Je potrebné overenie pre spustenie odstránenia účtu'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Potvrď odstránenie účtu'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Tento účet je prepojený s inými aplikáciami Ente, ak nejaké používaš.\n\nTvoje nahrané údaje vo všetkých Ente aplikáciách budú naplánované na odstránenie a tvoj účet bude natrvalo odstránený.'; @override - String get delete => 'Delete'; + String get delete => 'Odstrániť'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Vytvoriť nový účet'; @override - String get password => 'Password'; + String get password => 'Heslo'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Potvrdiť heslo'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Sila hesla: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ako ste sa dozvedeli o Ente? (voliteľné)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Nesledujeme inštalácie aplikácie. Veľmi by nám pomohlo, keby ste nám povedali, ako ste sa o nás dozvedeli!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Podmienky používania'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Zásady ochrany osobných údajov'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Rozumiem, že ak stratím alebo zabudnem heslo, môžem stratiť svoje údaje, pretože moje údaje sú šifrované end-to-end.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Šifrovanie'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Prihlásenie'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Vitajte späť!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Kliknutím na prihlásenie, súhlasím s podmienkami používania a zásadami ochrany osobných údajov'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Žiadne internetové pripojenie'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Skontrolujte svoje internetové pripojenie a skúste to znova.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Overenie zlyhalo, skúste to znova'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Resetovať heslo'; @override String get recreatePasswordBody => - '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).'; + 'Aktuálne zariadenie nie je dostatočne výkonné na overenie vášho hesla, avšak vieme ho regenerovať spôsobom, ktorý funguje vo všetkých zariadeniach.\n\nPrihláste sa pomocou kľúča na obnovenie a znovu vygenerujte svoje heslo (ak si prajete, môžete znova použiť rovnaké).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Použiť kľúč na obnovenie'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Zabudnuté heslo'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Zmeniť e-mail'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Overiť email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Odoslali sme email na adresu $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Ak chcete obnoviť svoje heslo, najskôr overte svoj email.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Skontrolujte svoju doručenú poštu (a spam) pre dokončenie overenia'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Klepnutím zadajte kód'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Odoslať email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Znovu odoslať email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Overenie stále prebieha'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Relácia vypršala'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Vaša relácia vypršala. Prosím, prihláste sa znovu.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Overenie pomocou passkey'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Čakanie na overenie...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Skúsiť znova'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Overiť stav'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Prihlásenie pomocou TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Obnoviť účet'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Nastaviť heslo'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Zmeniť heslo'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Obnoviť heslo'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Šifrovacie kľúče'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Zadajte heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Zadajte nové heslo, ktoré môžeme použiť na šifrovanie vašich údajov'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Ente neukladá tohto heslo. V prípade, že ho zabudnete, nie sme schopní rozšifrovať vaše údaje'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Ako to funguje'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Generovanie šifrovacích kľúčov...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Heslo bolo úspešne zmenené'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Odhlásiť sa z iných zariadení'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ak si myslíš, že by niekto mohol poznať tvoje heslo, môžeš vynútiť odhlásenie všetkých ostatných zariadení používajúcich tvoj účet.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Odhlásiť iné zariadenie'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Neodhlasovať'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Generovanie šifrovacích kľúčov...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Pokračovať'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Slabo zabezpečené zariadenie'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Ospravedlňujeme sa, v tomto zariadení sme nemohli generovať bezpečnostné kľúče.\n\nzaregistrujte sa z iného zariadenia.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Skopírovaný kód pre obnovenie do schránky'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kľúč pre obnovenie'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ak zabudnete heslo, jediným spôsobom, ako môžete obnoviť svoje údaje, je tento kľúč.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'My tento kľúč neuchovávame, uložte si tento kľúč obsahujúci 24 slov na bezpečnom mieste.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Urobiť to neskôr'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Uložiť kľúč'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kľúč na obnovenie uložený v priečinku Stiahnutých súborov!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Nemáte kľúč pre obnovenie?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Dvojfaktorové overovanie'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Zadajte 6-miestny kód z\nvašej overovacej aplikácie'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Stratené zariadenie?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Vložte váš kód pre obnovenie'; @override - String get recover => 'Recover'; + String get recover => 'Obnoviť'; + + @override + String get loggingOut => 'Odhlasovanie...'; + + @override + String get immediately => 'Okamžite'; + + @override + String get appLock => 'Zámok aplikácie'; + + @override + String get autoLock => 'Automatické uzamknutie'; + + @override + String get noSystemLockFound => 'Nenájdená žiadna zámka obrazovky'; + + @override + String get deviceLockEnablePreSteps => + 'Pre povolenie zámku zariadenia, nastavte prístupový kód zariadenia alebo zámok obrazovky v nastaveniach systému.'; + + @override + String get appLockDescription => + 'Vyberte si medzi predvolenou zámkou obrazovky vášho zariadenia a vlastnou zámkou obrazovky s PIN kódom alebo heslom.'; + + @override + String get deviceLock => 'Zámok zariadenia'; + + @override + String get pinLock => 'Zámok PIN'; + + @override + String get autoLockFeatureDescription => + 'Čas, po ktorom sa aplikácia uzamkne po nečinnosti'; + + @override + String get hideContent => 'Skryť obsah'; + + @override + String get hideContentDescriptionAndroid => + 'Skrýva obsah v prepínači aplikácii a zakazuje snímky obrazovky'; + + @override + String get hideContentDescriptioniOS => 'Skrýva obsah v prepínači aplikácii'; + + @override + String get tooManyIncorrectAttempts => 'Príliš veľa chybných pokusov'; + + @override + String get tapToUnlock => 'Ťuknutím odomknete'; + + @override + String get areYouSureYouWantToLogout => 'Naozaj sa chcete odhlásiť?'; + + @override + String get yesLogout => 'Áno, odhlásiť sa'; + + @override + String get authToViewSecrets => + 'Pre zobrazenie vašich tajných údajov sa musíte overiť'; + + @override + String get next => 'Ďalej'; + + @override + String get setNewPassword => 'Nastaviť nové heslo'; + + @override + String get enterPin => 'Zadajte PIN'; + + @override + String get setNewPin => 'Nastaviť nový PIN'; + + @override + String get confirm => 'Potvrdiť'; + + @override + String get reEnterPassword => 'Zadajte heslo znova'; + + @override + String get reEnterPin => 'Zadajte PIN znova'; + + @override + String get androidBiometricHint => 'Overiť identitu'; + + @override + String get androidBiometricNotRecognized => 'Nerozpoznané. Skúste znova.'; + + @override + String get androidBiometricSuccess => 'Overenie úspešné'; + + @override + String get androidCancelButton => 'Zrušiť'; + + @override + String get androidSignInTitle => 'Vyžaduje sa overenie'; + + @override + String get androidBiometricRequiredTitle => 'Vyžaduje sa biometria'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Vyžadujú sa poverenia zariadenia'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Vyžadujú sa poverenia zariadenia'; + + @override + String get goToSettings => 'Prejsť do nastavení'; + + @override + String get androidGoToSettingsDescription => + 'Overenie pomocou biometrie nie je na vašom zariadení nastavené. Prejdite na \'Nastavenie > Zabezpečenie\' a pridajte overenie pomocou biometrie.'; + + @override + String get iOSLockOut => + 'Overenie pomocou biometrie je zakázané. Zamknite a odomknite svoju obrazovku, aby ste ho povolili.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'Email already registered.'; + + @override + String get emailNotRegistered => 'Email not registered.'; + + @override + String get thisEmailIsAlreadyInUse => 'Tento e-mail sa už používa'; + + @override + String emailChangedTo(String newEmail) { + return 'Emailová adresa bola zmenená na $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Overenie zlyhalo. Skúste to znova'; + + @override + String get authenticationSuccessful => 'Overenie sa podarilo!'; + + @override + String get sessionExpired => 'Relácia vypršala'; + + @override + String get incorrectRecoveryKey => 'Nesprávny kľúč na obnovenie'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Kľúč na obnovenie, ktorý ste zadali, je nesprávny'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Dvojfaktorové overovanie bolo úspešne obnovené'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Platnosť overovacieho kódu uplynula'; + + @override + String get incorrectCode => 'Neplatný kód'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Ľutujeme, zadaný kód je nesprávny'; + + @override + String get developerSettings => 'Nastavenia pre vývojárov'; + + @override + String get serverEndpoint => 'Endpoint servera'; + + @override + String get invalidEndpoint => 'Neplatný endpoint'; + + @override + String get invalidEndpointMessage => + 'Ospravedlňujeme sa, endpoint, ktorý ste zadali, je neplatný. Zadajte platný endpoint a skúste to znova.'; + + @override + String get endpointUpdatedMessage => 'Endpoint úspešne aktualizovaný'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart index 8a1a1e1dbe..3a2c2fc4f7 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sr.dart @@ -103,333 +103,528 @@ class StringsLocalizationsSr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Да ли желите да ово сачувате у складиште (фасцикли за преузимање подразумевано)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Унесите Ваш нови имејл'; @override - String get email => 'Email'; + String get email => 'Имејл'; @override - String get verify => 'Verify'; + String get verify => 'Верификуј'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Погрешна имејл адреса'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Унесите важећи имејл.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Молимо сачекајте...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Верификујте лозинку'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Неисправна лозинка'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Пробајте поново'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Унеси лозинку'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Унесите лозинку'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Активне сесије'; @override - String get oops => 'Oops'; + String get oops => 'Упс'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Нешто је пошло наопако. Покушајте поново'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Ово ће вас одјавити из овог уређаја!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Ово ће вас одјавити из овог уређаја:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Прекинути сесију?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Прекини'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Овај уређај'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Направи налог'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Слабо'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Умерено'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Јако'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Избриши налог'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Жао нам је што одлазите. Да ли се суочавате са неком грешком?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Да, послати повратне информације'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Не, избрисати налог'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Молимо вас да се аутентификујете за брисање рачуна'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Потврда брисања рачуна'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Овај налог је повезан са другим Ente апликацијама, ако користите било коју.\n\nВаши преношени подаци, на свим Ente апликацијама биће заказани за брисање, и ваш рачун ће се трајно избрисати.'; @override - String get delete => 'Delete'; + String get delete => 'Обриши'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Креирај нови налог'; @override - String get password => 'Password'; + String get password => 'Лозинка'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Потврдите лозинку'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Снага лозинке: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Како сте чули о Ente? (опционо)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Не пратимо инсталацију апликације. Помогло би да нам кажеш како си нас нашао!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Прихватам услове сервиса и политику приватности'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Услови'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Политика приватности'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Разумем да ако изгубим лозинку, могу изгубити своје податке пошто су шифрирани од краја до краја.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Шифровање'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Пријави се'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Добродошли назад!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Кликом на пријаву, прихватам услове сервиса и политику приватности'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Нема интернет везе'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Провери своју везу са интернетом и покушај поново.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Неуспешна верификација, покушајте поново'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Поново креирати лозинку'; @override String get recreatePasswordBody => - '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).'; + 'Тренутни уређај није довољно моћан да потврди вашу лозинку, али можемо регенерирати на начин који ради са свим уређајима.\n\nПријавите се помоћу кључа за опоравак и обновите своју лозинку (можете поново користити исту ако желите).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Користите кључ за опоравак'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Заборавио сам лозинку'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Промени имејл'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Потврди имејл'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Послали смо имејл на $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Да бисте ресетовали лозинку, прво потврдите свој имејл.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Молимо вас да проверите примљену пошту (и нежељену пошту) да бисте довршили верификацију'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Пипните да бисте унели кôд'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Шаљи имејл'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Поново послати имејл'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Верификација је још у току'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Сесија је истекла'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Ваша сесија је истекла. Молимо пријавите се поново.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Верификација сигурносном кључем'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Чека се верификација...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Покушај поново'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Провери статус'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Пријава са TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Опоравак налога'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Постави лозинку'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Промени лозинку'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Ресетуј лозинку'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Кључеве шифровања'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Унесите лозинку за употребу за шифровање ваших података'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Унесите нову лозинку за употребу за шифровање ваших података'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Не чувамо ову лозинку, па ако је заборавите, не можемо дешифрирати ваше податке'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Како то функционише'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Генерисање кључева за шифровање...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Лозинка је успешно промењена'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Одјави се из других уређаја'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Ако мислиш да неко може знати твоју лозинку, можеш приморати одјављивање све остале уређаје које користе твој налог.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Одјави друге уређаје'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Не одјави'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Генерисање кључева за шифровање...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Настави'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Уређај није сигуран'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Извините, не можемо да генеришемо сигурне кључеве на овом уређају.\n\nМолимо пријавите се са другог уређаја.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Кључ за опоравак копирано у остави'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Резервни Кључ'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Ако заборавите лозинку, једини начин на који можете повратити податке је са овим кључем.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Не чувамо овај кључ, молимо да сачувате кључ од 24 речи на сигурном месту.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Уради то касније'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Сачувај кључ'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Кључ за опоравак сачуван у фасцикли за преузимање!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Немате кључ за опоравак?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Дво-факторска аутентификација'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Унесите 6-цифрени кôд из\nапликације за аутентификацију'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Узгубили сте уређај?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Унети кључ за опоравак'; @override - String get recover => 'Recover'; + String get recover => 'Опорави'; + + @override + String get loggingOut => 'Одјављивање...'; + + @override + String get immediately => 'Одмах'; + + @override + String get appLock => 'Закључавање апликације'; + + @override + String get autoLock => 'Ауто-закључавање'; + + @override + String get noSystemLockFound => 'Није пронађено ниједно закључавање система'; + + @override + String get deviceLockEnablePreSteps => + 'Да бисте омогућили закључавање уређаја, молимо вас да подесите шифру уређаја или закључавање екрана у системским подешавањима.'; + + @override + String get appLockDescription => + 'Изаберите између заданог закључавање екрана вашег уређаја и прилагођени екран за закључавање са ПИН-ом или лозинком.'; + + @override + String get deviceLock => 'Закључавање уређаја'; + + @override + String get pinLock => 'ПИН клокирање'; + + @override + String get autoLockFeatureDescription => + 'Време након којег се апликација блокира након што је постављенеа у позадину'; + + @override + String get hideContent => 'Сакриј садржај'; + + @override + String get hideContentDescriptionAndroid => + 'Сакрива садржај апликације у пребацивање апликација и онемогућује снимке екрана'; + + @override + String get hideContentDescriptioniOS => + 'Сакрива садржај апликације у пребацивање апликација'; + + @override + String get tooManyIncorrectAttempts => 'Превише погрешних покушаја'; + + @override + String get tapToUnlock => 'Додирните да бисте откључали'; + + @override + String get areYouSureYouWantToLogout => 'Да ли сте сигурни да се одјавите?'; + + @override + String get yesLogout => 'Да, одјави ме'; + + @override + String get authToViewSecrets => + 'Аутентификујте се да бисте прегледали Ваше тајне'; + + @override + String get next => 'Следеће'; + + @override + String get setNewPassword => 'Постави нову лозинку'; + + @override + String get enterPin => 'Унеси ПИН'; + + @override + String get setNewPin => 'Постави нови ПИН'; + + @override + String get confirm => 'Потврди'; + + @override + String get reEnterPassword => 'Поново унеси лозинку'; + + @override + String get reEnterPin => 'Поново унеси ПИН'; + + @override + String get androidBiometricHint => 'Потврдите идентитет'; + + @override + String get androidBiometricNotRecognized => + 'Нисмо препознали. Покушати поново.'; + + @override + String get androidBiometricSuccess => 'Успех'; + + @override + String get androidCancelButton => 'Откажи'; + + @override + String get androidSignInTitle => 'Потребна аутентификација'; + + @override + String get androidBiometricRequiredTitle => 'Потребна је биометрија'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Потребни су акредитиви уређаја'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Потребни су акредитиви уређаја'; + + @override + String get goToSettings => 'Иди на поставке'; + + @override + String get androidGoToSettingsDescription => + 'Биометријска аутентификација није постављена на вашем уређају. Идите на \"Подешавања> Сигурност\" да бисте додали биометријску аутентификацију.'; + + @override + String get iOSLockOut => + 'Биометријска аутентификација је онемогућена. Закључајте и откључите екран да бисте је омогућили.'; + + @override + String get iOSOkButton => 'У реду'; + + @override + String get emailAlreadyRegistered => 'Имејл је већ регистрован.'; + + @override + String get emailNotRegistered => 'Имејл није регистрован.'; + + @override + String get thisEmailIsAlreadyInUse => 'Овај имејл је већ у употреби'; + + @override + String emailChangedTo(String newEmail) { + return 'Имејл промењен на $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Аутентификација није успела, покушајте поново'; + + @override + String get authenticationSuccessful => 'Успешна аутентификација!'; + + @override + String get sessionExpired => 'Сесија је истекла'; + + @override + String get incorrectRecoveryKey => 'Нетачан кључ за опоравак'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Унети кључ за опоравак је натачан'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Двофакторска аутентификација успешно рисетирана'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Ваш верификациони кôд је истекао'; + + @override + String get incorrectCode => 'Погрешан кôд'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => 'Унет кôд није добар'; + + @override + String get developerSettings => 'Подешавања за програмере'; + + @override + String get serverEndpoint => 'Крајња тачка сервера'; + + @override + String get invalidEndpoint => 'Погрешна крајња тачка'; + + @override + String get invalidEndpointMessage => + 'Извини, крајња тачка коју си унео је неважећа. Унеси важећу крајњу тачку и покушај поново.'; + + @override + String get endpointUpdatedMessage => 'Крајна тачка успешно ажурирана'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart index f20dce0c0c..96d85243d0 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_sv.dart @@ -103,333 +103,526 @@ class StringsLocalizationsSv extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Vill du spara detta till din lagringsmapp (Nedladdningsmappen som standard)?'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Ange din nya e-postadress'; @override - String get email => 'Email'; + String get email => 'E-post'; @override - String get verify => 'Verify'; + String get verify => 'Verifiera'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Ogiltig e-postadress'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Ange en giltig e-postadress.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vänligen vänta...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Bekräfta lösenord'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Felaktigt lösenord'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Försök igen'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Ange lösenord'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Ange ditt lösenord'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktiva sessioner'; @override - String get oops => 'Oops'; + String get oops => 'Hoppsan'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Något gick fel, vänligen försök igen'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Detta kommer att logga ut dig från den här enheten!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Detta kommer att logga ut dig från följande enhet:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Avsluta session?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Avsluta'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Den här enheten'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Skapa konto'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Svag'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Måttligt'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Stark'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Radera konto'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Vi kommer att vara ledsna över att se dig gå. Har du något problem?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Ja, skicka feedback'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Nej, radera konto'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Vänligen autentisera för att initiera borttagning av konto'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Bekräfta radering av kontot'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.'; @override - String get delete => 'Delete'; + String get delete => 'Radera'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Skapa nytt konto'; @override - String get password => 'Password'; + String get password => 'Lösenord'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Bekräfta lösenord'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Lösenordsstyrka: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Hur hörde du talas om Ente? (valfritt)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Jag samtycker till användarvillkoren och integritetspolicyn'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Villkor'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Integritetspolicy'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är end-to-end-krypterad.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Kryptering'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Logga in'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Välkommen tillbaka!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Jag samtycker till användarvillkoren och integritetspolicyn'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Ingen internetanslutning'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Kontrollera din internetanslutning och försök igen.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Verifiering misslyckades, vänligen försök igen'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Återskapa lösenord'; @override String get recreatePasswordBody => - '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).'; + 'Denna enhet är inte tillräckligt kraftfull för att verifiera ditt lösenord, men vi kan återskapa det på ett sätt som fungerar med alla enheter.\n\nLogga in med din återställningsnyckel och återskapa ditt lösenord (du kan använda samma igen om du vill).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Använd återställningsnyckel'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Glömt lösenord'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Ändra e-postadress'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Verifiera e-postadress'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Vi har skickat ett mail till $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'För att återställa ditt lösenord måste du först bekräfta din e-postadress.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Tryck för att ange kod'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Skicka e-post'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Skicka e-post igen'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Verifiering pågår fortfarande'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Sessionen har gått ut'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Din session har upphört. Logga in igen.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Verifiering med inloggningsnyckel'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Väntar på verifiering...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Försök igen'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Kontrollera status'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Logga in med TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Återställ konto'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Ställ in lösenord'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Ändra lösenord'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Återställ lösenord'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Krypteringsnycklar'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Ange ett lösenord som vi kan använda för att kryptera din data'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Ange ett nytt lösenord som vi kan använda för att kryptera din data'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Vi lagrar inte detta lösenord, så om du glömmer bort det, kan vi inte dekryptera dina data'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Så här fungerar det'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Skapar krypteringsnycklar...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Lösenordet har ändrats'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Logga ut från andra enheter'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Om du tror att någon kanske känner till ditt lösenord kan du tvinga alla andra enheter med ditt konto att logga ut.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Logga ut andra enheter'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Logga inte ut'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Skapar krypteringsnycklar...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Fortsätt'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Osäker enhet'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Återställningsnyckel kopierad till urklipp'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Återställningsnyckel'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Gör detta senare'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Spara nyckel'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Återställningsnyckel sparad i nedladdningsmappen!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Ingen återställningsnyckel?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Tvåfaktorsautentisering'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Ange den 6-siffriga koden från din autentiseringsapp'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Förlorad enhet?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Ange din återställningsnyckel'; @override - String get recover => 'Recover'; + String get recover => 'Återställ'; + + @override + String get loggingOut => 'Loggar ut...'; + + @override + String get immediately => 'Omedelbart'; + + @override + String get appLock => 'Applås'; + + @override + String get autoLock => 'Automatisk låsning'; + + @override + String get noSystemLockFound => 'Inget systemlås hittades'; + + @override + String get deviceLockEnablePreSteps => + 'För att aktivera enhetslås, vänligen ställ in enhetens lösenord eller skärmlås i dina systeminställningar.'; + + @override + String get appLockDescription => + 'Choose between your device\'s default lock screen and a custom lock screen with a PIN or password.'; + + @override + String get deviceLock => 'Enhetslås'; + + @override + String get pinLock => 'Pinkodslås'; + + @override + String get autoLockFeatureDescription => + 'Time after which the app locks after being put in the background'; + + @override + String get hideContent => 'Dölj innehåll'; + + @override + String get hideContentDescriptionAndroid => + 'Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar'; + + @override + String get hideContentDescriptioniOS => 'Döljer appinnehåll i app-växlaren'; + + @override + String get tooManyIncorrectAttempts => 'För många felaktiga försök'; + + @override + String get tapToUnlock => 'Tryck för att låsa upp'; + + @override + String get areYouSureYouWantToLogout => + 'Är du säker på att du vill logga ut?'; + + @override + String get yesLogout => 'Ja, logga ut'; + + @override + String get authToViewSecrets => + 'Autentisera för att visa din återställningsnyckel'; + + @override + String get next => 'Nästa'; + + @override + String get setNewPassword => 'Ställ in nytt lösenord'; + + @override + String get enterPin => 'Ange PIN-kod'; + + @override + String get setNewPin => 'Ställ in ny PIN-kod'; + + @override + String get confirm => 'Bekräfta'; + + @override + String get reEnterPassword => 'Ange lösenord igen'; + + @override + String get reEnterPin => 'Ange PIN-kod igen'; + + @override + String get androidBiometricHint => 'Verifiera identitet'; + + @override + String get androidBiometricNotRecognized => 'Ej godkänd. Försök igen.'; + + @override + String get androidBiometricSuccess => 'Slutförd'; + + @override + String get androidCancelButton => 'Avbryt'; + + @override + String get androidSignInTitle => 'Obligatorisk autentisering'; + + @override + String get androidBiometricRequiredTitle => 'Biometriska uppgifter krävs'; + + @override + String get androidDeviceCredentialsRequiredTitle => 'Enhetsuppgifter krävs'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Enhetsuppgifter krävs'; + + @override + String get goToSettings => 'Gå till inställningar'; + + @override + String get androidGoToSettingsDescription => + 'Biometrisk autentisering är inte konfigurerad på din enhet. Gå till \"Inställningar > Säkerhet\" för att lägga till biometrisk autentisering.'; + + @override + String get iOSLockOut => + 'Biometrisk autentisering är inaktiverat. Lås och lås upp din skärm för att aktivera den.'; + + @override + String get iOSOkButton => 'OK'; + + @override + String get emailAlreadyRegistered => 'E-postadress redan registrerad.'; + + @override + String get emailNotRegistered => 'E-postadress ej registrerad.'; + + @override + String get thisEmailIsAlreadyInUse => 'Denna e-postadress används redan'; + + @override + String emailChangedTo(String newEmail) { + return 'E-post ändrad till $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Autentisering misslyckades, vänligen försök igen'; + + @override + String get authenticationSuccessful => 'Autentisering lyckades!'; + + @override + String get sessionExpired => 'Sessionen har gått ut'; + + @override + String get incorrectRecoveryKey => 'Felaktig återställningsnyckel'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Återställningsnyckeln du angav är felaktig'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Tvåfaktorsautentisering återställd'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Din verifieringskod har upphört att gälla'; + + @override + String get incorrectCode => 'Felaktig kod'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Tyvärr, den kod som du har angett är felaktig'; + + @override + String get developerSettings => 'Utvecklarinställningar'; + + @override + String get serverEndpoint => 'Serverns slutpunkt'; + + @override + String get invalidEndpoint => 'Ogiltig slutpunkt'; + + @override + String get invalidEndpointMessage => + 'Tyvärr, slutpunkten du angav är ogiltig. Ange en giltig slutpunkt och försök igen.'; + + @override + String get endpointUpdatedMessage => 'Slutpunkten har uppdaterats'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart index c1b02a9f9f..c5be60b8db 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_tr.dart @@ -103,333 +103,530 @@ class StringsLocalizationsTr extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Bunu belleğinize kaydetmek ister misiniz? (İndirilenler klasörü varsayılandır)'; @override - String get enterNewEmailHint => 'Enter your new email address'; + String get enterNewEmailHint => 'Yeni e-posta adresinizi girin'; @override - String get email => 'Email'; + String get email => 'E-Posta'; @override - String get verify => 'Verify'; + String get verify => 'Doğrula'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Geçersiz e-posta adresi'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => 'Lütfen geçerli bir e-posta adresi girin.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Lütfen bekleyin...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Şifreyi doğrulayın'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Yanlış şifre'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Lütfen tekrar deneyin'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Şifreyi girin'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Parolanızı girin'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Aktif oturumlar'; @override - String get oops => 'Oops'; + String get oops => 'Hay aksi'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Bir şeyler ters gitti, lütfen tekrar deneyin'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Bu sizin bu cihazdaki oturumunuzu kapatacaktır!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Bu, aşağıdaki cihazdan çıkış yapmanızı sağlayacaktır:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Oturumu sonlandır?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Sonlandır'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Bu cihaz'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Hesap oluştur'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Zayıf'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Orta'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Güçlü'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Hesabı sil'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Sizin gittiğinizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Evet, geri bildirimi gönder'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Hayır, hesabı sil'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Hesap silme işlemini yapabilmek için lütfen kimliğinizi doğrulayın'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Hesap silme işlemini onayla'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Kullandığınız Ente uygulamaları varsa bu hesap diğer Ente uygulamalarıyla bağlantılıdır.\n\nTüm Ente uygulamalarına yüklediğiniz veriler ve hesabınız kalıcı olarak silinecektir.'; @override - String get delete => 'Delete'; + String get delete => 'Sil'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Yeni hesap oluşturun'; @override - String get password => 'Password'; + String get password => 'Şifre'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Şifreyi onayla'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Şifre gücü: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => 'Ente\'yi nereden duydunuz? (opsiyonel)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Biz uygulama kurulumlarını takip etmiyoruz. Bizi nereden duyduğunuzdan bahsetmeniz bize çok yardımcı olacak!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Kullanım şartlarını ve gizlilik politikasını kabul ediyorum'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Şartlar'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Gizlilik Politikası'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Eğer şifremi kaybedersem, verilerim uçtan uca şifrelendiğinden verilerimi kaybedebileceğimi anladım.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Şifreleme'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Giriş yapın'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Tekrar hoş geldiniz!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Giriş yaparak, kullanım şartları nı ve gizlilik politikası nı onaylıyorum'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'İnternet bağlantısı yok'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Lütfen internet bağlantınızı kontrol edin ve yeniden deneyin.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Doğrulama başarısız oldu, lütfen tekrar deneyin'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Şifreyi yeniden oluştur'; @override String get recreatePasswordBody => - '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).'; + 'Mevcut cihaz şifrenizi doğrulayacak kadar güçlü değil, ancak tüm cihazlarla çalışacak şekilde yeniden oluşturabiliriz.\n\nLütfen kurtarma anahtarınızı kullanarak giriş yapın ve şifrenizi yeniden oluşturun (isterseniz aynı şifreyi tekrar kullanabilirsiniz).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Kurtarma anahtarını kullan'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Şifremi unuttum'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'E-posta adresini değiştir'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'E-posta adresini doğrulayın'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '$email adresine bir posta gönderdik'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Şifrenizi sıfırlamak için lütfen önce e-postanızı doğrulayın.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Doğrulamayı tamamlamak için lütfen gelen kutunuzu (ve spam kutunuzu) kontrol edin'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Kodu girmek için dokunun'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'E-posta gönder'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'E-postayı yeniden gönder'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Doğrulama hala bekliyor'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Oturum süresi doldu'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Oturum süreniz doldu. Tekrar giriş yapın.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Geçiş anahtarı doğrulaması'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Doğrulama bekleniyor...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Tekrar deneyin'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Durumu kontrol et'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'TOTP ile giriş yap'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Hesap kurtarma'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Şifre belirleyin'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Şifreyi değiştirin'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Şifreyi sıfırlayın'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Şifreleme anahtarları'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Verilerinizi şifrelemek için kullanabileceğimiz bir şifre girin'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Verilerinizi şifrelemek için kullanabileceğimiz yeni bir şifre girin'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Bu şifreyi saklamıyoruz, bu nedenle unutursanız, verilerinizin şifresini çözemeyiz'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Nasıl çalışır'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => + 'Şifreleme anahtarları oluşturuluyor...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Şifre başarıyla değiştirildi'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Diğer cihazlardan çıkış yap'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Eğer başka birisinin parolanızı bildiğini düşünüyorsanız, diğer tüm cihazları hesabınızdan çıkışa zorlayabilirsiniz.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Diğer cihazlardan çıkış yap'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Çıkış yapma'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => + 'Şifreleme anahtarları üretiliyor...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Devam et'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Güvenli olmayan cihaz'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Üzgünüz, bu cihazda güvenli anahtarlar oluşturamadık.\n\nlütfen farklı bir cihazdan kaydolun.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Kurtarma anahtarı panoya kopyalandı'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Kurtarma Anahtarı'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Eğer şifrenizi unutursanız, verilerinizi kurtarabileceğiniz tek yol bu anahtardır.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Biz bu anahtarı saklamıyoruz, lütfen. bu 24 kelimelik anahtarı güvenli bir yerde saklayın.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Bunu daha sonra yap'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Anahtarı kaydet'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => + 'Kurtarma anahtarı İndirilenler klasörüne kaydedildi!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Kurtarma anahtarınız yok mu?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'İki faktörlü kimlik doğrulama'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Kimlik doğrulayıcı uygulamanızdaki 6 haneli doğrulama kodunu girin'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Cihazınızı mı kaybettiniz?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Kurtarma anahtarınızı girin'; @override - String get recover => 'Recover'; + String get recover => 'Kurtar'; + + @override + String get loggingOut => 'Çıkış yapılıyor...'; + + @override + String get immediately => 'Hemen'; + + @override + String get appLock => 'Uygulama kilidi'; + + @override + String get autoLock => 'Otomatik Kilit'; + + @override + String get noSystemLockFound => 'Sistem kilidi bulunamadı'; + + @override + String get deviceLockEnablePreSteps => + 'Cihaz kilidini etkinleştirmek için, lütfen cihaz şifresini veya ekran kilidini ayarlayın.'; + + @override + String get appLockDescription => + 'Cihazınızın varsayılan kilit ekranı ile PIN veya parola içeren özel bir kilit ekranı arasında seçim yapın.'; + + @override + String get deviceLock => 'Cihaz kilidi'; + + @override + String get pinLock => 'Pin kilidi'; + + @override + String get autoLockFeatureDescription => + 'Uygulamayı arka plana attıktan sonra kilitlendiği süre'; + + @override + String get hideContent => 'İçeriği gizle'; + + @override + String get hideContentDescriptionAndroid => + 'Uygulama değiştiricide bulunan uygulama içeriğini gizler ve ekran görüntülerini devre dışı bırakır'; + + @override + String get hideContentDescriptioniOS => + 'Uygulama değiştiricideki uygulama içeriğini gizler'; + + @override + String get tooManyIncorrectAttempts => 'Çok fazla hatalı deneme'; + + @override + String get tapToUnlock => 'Açmak için dokun'; + + @override + String get areYouSureYouWantToLogout => + 'Çıkış yapmak istediğinize emin misiniz?'; + + @override + String get yesLogout => 'Evet, çıkış yap'; + + @override + String get authToViewSecrets => + 'Kodlarınızı görmek için lütfen kimlik doğrulaması yapın'; + + @override + String get next => 'Sonraki'; + + @override + String get setNewPassword => 'Yeni şifre belirle'; + + @override + String get enterPin => 'PIN Girin'; + + @override + String get setNewPin => 'Yeni PIN belirleyin'; + + @override + String get confirm => 'Doğrula'; + + @override + String get reEnterPassword => 'Şifrenizi tekrar girin'; + + @override + String get reEnterPin => 'PIN\'inizi tekrar girin'; + + @override + String get androidBiometricHint => 'Kimliği doğrula'; + + @override + String get androidBiometricNotRecognized => 'Tanınmadı. Tekrar deneyin.'; + + @override + String get androidBiometricSuccess => 'Başarılı'; + + @override + String get androidCancelButton => 'İptal et'; + + @override + String get androidSignInTitle => 'Kimlik doğrulaması gerekli'; + + @override + String get androidBiometricRequiredTitle => 'Biyometrik gerekli'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Cihaz kimlik bilgileri gerekli'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Cihaz kimlik bilgileri gerekmekte'; + + @override + String get goToSettings => 'Ayarlara git'; + + @override + String get androidGoToSettingsDescription => + 'Biyometrik kimlik doğrulama cihazınızda ayarlanmamış. Biyometrik kimlik doğrulama eklemek için \'Ayarlar > Güvenlik\' bölümüne gidin.'; + + @override + String get iOSLockOut => + 'Biyometrik kimlik doğrulama devre dışı. Etkinleştirmek için lütfen ekranınızı kilitleyin ve kilidini açın.'; + + @override + String get iOSOkButton => 'Tamam'; + + @override + String get emailAlreadyRegistered => 'E-posta zaten kayıtlı.'; + + @override + String get emailNotRegistered => 'E-posta kayıtlı değil.'; + + @override + String get thisEmailIsAlreadyInUse => 'Bu e-posta zaten kullanılıyor'; + + @override + String emailChangedTo(String newEmail) { + return 'E-posta $newEmail olarak değiştirildi'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Kimlik doğrulama başarısız oldu, lütfen tekrar deneyin'; + + @override + String get authenticationSuccessful => 'Kimlik doğrulama başarılı!'; + + @override + String get sessionExpired => 'Oturum süresi doldu'; + + @override + String get incorrectRecoveryKey => 'Yanlış kurtarma kodu'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Girdiğiniz kurtarma kodu yanlış'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'İki faktörlü kimlik doğrulama başarıyla sıfırlandı'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => + 'Doğrulama kodunuzun süresi doldu'; + + @override + String get incorrectCode => 'Yanlış kod'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Üzgünüz, girdiğiniz kod yanlış'; + + @override + String get developerSettings => 'Geliştirici ayarları'; + + @override + String get serverEndpoint => 'Sunucu uç noktası'; + + @override + String get invalidEndpoint => 'Geçersiz uç nokta'; + + @override + String get invalidEndpointMessage => + 'Üzgünüz, girdiğiniz uç nokta geçersiz. Lütfen geçerli bir uç nokta girin ve tekrar deneyin.'; + + @override + String get endpointUpdatedMessage => 'Uç nokta başarıyla güncellendi'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart index d231567f04..a55e1a242c 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_vi.dart @@ -103,333 +103,526 @@ class StringsLocalizationsVi extends StringsLocalizations { @override String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + 'Bạn có muốn lưu vào bộ nhớ không (Mặc định lưu vào thư mục Tải về)?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => 'Thư điện tử'; @override - String get verify => 'Verify'; + String get verify => 'Xác minh'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => 'Địa chỉ email không hợp lệ'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => + 'Xin vui lòng nhập một địa chỉ email hợp lệ.'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => 'Vui lòng chờ...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => 'Xác nhận mật khẩu'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => 'Mật khẩu không đúng'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => 'Vui lòng thử lại'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => 'Nhập mật khẩu'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => 'Nhập mật khẩu của bạn'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => 'Các phiên làm việc hiện tại'; @override - String get oops => 'Oops'; + String get oops => 'Rất tiếc'; @override String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + 'Phát hiện có lỗi, xin thử lại'; @override String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị này!'; @override String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + 'Thao tác này sẽ đăng xuất bạn khỏi thiết bị sau:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => 'Kết thúc phiên?'; @override - String get terminate => 'Terminate'; + String get terminate => 'Kết thúc'; @override - String get thisDevice => 'This device'; + String get thisDevice => 'Thiết bị này'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Tạo tài khoản'; @override - String get weakStrength => 'Weak'; + String get weakStrength => 'Yếu'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => 'Trung bình'; @override - String get strongStrength => 'Strong'; + String get strongStrength => 'Mạnh'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Xoá tài khoản'; @override String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + 'Chúng tôi sẽ rất tiếc khi thấy bạn đi. Bạn đang phải đối mặt với một số vấn đề?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => 'Có, gửi phản hồi'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => 'Không, xóa tài khoản'; @override String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + 'Vui lòng xác thực để bắt đầu xóa tài khoản'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => 'Xác nhận xóa tài khoản'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + 'Tài khoản này được liên kết với các ứng dụng Ente trên các nền tảng khác, nếu bạn có sử dụng.\n\nDữ liệu đã tải lên của bạn, trên mọi nền tảng, sẽ bị lên lịch xóa và tài khoản của bạn sẽ bị xóa vĩnh viễn.'; @override - String get delete => 'Delete'; + String get delete => 'Xóa'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => 'Tạo tài khoản mới'; @override - String get password => 'Password'; + String get password => 'Mật khẩu'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => 'Xác nhận mật khẩu'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return 'Độ mạnh mật khẩu: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => + 'Bạn biết đến Ente bằng cách nào? (không bắt buộc)'; @override String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + 'Chúng tôi không theo dõi lượt cài đặt ứng dụng. Sẽ rất hữu ích nếu bạn cho chúng tôi biết nơi bạn tìm thấy chúng tôi!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + 'Tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => 'Điều khoản'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Chính sách bảo mật'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + 'Tôi hiểu rằng việc mất mật khẩu có thể đồng nghĩa với việc mất dữ liệu của tôi vì dữ liệu của tôi được mã hóa hai đầu.'; @override - String get encryption => 'Encryption'; + String get encryption => 'Mã hóa'; @override - String get logInLabel => 'Log in'; + String get logInLabel => 'Đăng nhập'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => 'Chào mừng trở lại!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + 'Bằng cách nhấp vào đăng nhập, tôi đồng ý với điều khoản dịch vụchính sách quyền riêng tư'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => 'Không có kết nối Internet'; @override String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + 'Vui lòng kiểm tra kết nối internet của bạn và thử lại.'; @override String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + 'Mã xác nhận thất bại. Vui lòng thử lại'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => 'Tạo lại mật khẩu'; @override String get recreatePasswordBody => - '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).'; + 'Thiết bị hiện tại không đủ mạnh để xác minh mật khẩu của bạn nhưng chúng tôi có thể tạo lại mật khẩu theo cách hoạt động với tất cả các thiết bị.\n\nVui lòng đăng nhập bằng khóa khôi phục và tạo lại mật khẩu của bạn (bạn có thể sử dụng lại cùng một mật khẩu nếu muốn).'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => 'Dùng khóa khôi phục'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => 'Quên mật khẩu'; @override - String get changeEmail => 'Change email'; + String get changeEmail => 'Thay đổi email'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => 'Xác nhận địa chỉ Email'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return 'Chúng tôi đã gửi thư đến $email'; } @override String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + 'Để đặt lại mật khẩu, vui lòng xác minh email của bạn trước.'; @override String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + 'Vui lòng kiểm tra hộp thư đến (và thư rác) của bạn để hoàn tất xác minh'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => 'Chạm để nhập mã'; @override - String get sendEmail => 'Send email'; + String get sendEmail => 'Gửi email'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => 'Gửi lại email'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => 'Đang chờ xác thực'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => 'Phiên làm việc hết hạn'; @override String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + 'Phiên làm việc hết hạn. Vui lòng đăng nhập lại.'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => 'Xác minh mã khóa'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => 'Đang chờ xác thực'; @override - String get tryAgain => 'Try again'; + String get tryAgain => 'Thử lại'; @override - String get checkStatus => 'Check status'; + String get checkStatus => 'Kiểm tra trạng thái'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => 'Đăng nhập bằng TOTP'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => 'Khôi phục tài khoản'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => 'Đặt mật khẩu'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => 'Thay đổi mật khẩu'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => 'Đặt lại mật khẩu'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => 'Khóa mã hóa'; @override String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + 'Nhập mật khẩu mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; @override String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + 'Nhập một mật khẩu mới mà chúng tôi có thể sử dụng để mã hóa dữ liệu của bạn'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + 'Chúng tôi không lưu trữ mật khẩu này, vì vậy nếu bạn quên, chúng tôi không thể giải mã dữ liệu của bạn'; @override - String get howItWorks => 'How it works'; + String get howItWorks => 'Cách thức hoạt động'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => 'Đang tạo khóa mã hóa...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => 'Thay đổi mật khẩu thành công'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; @override String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + 'Nếu bạn cho rằng ai đó có thể biết mật khẩu của mình, bạn có thể buộc đăng xuất tất cả các thiết bị khác đang sử dụng tài khoản của mình.'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => 'Đăng xuất khỏi các thiết bị khác'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => 'Không được đăng xuất'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => 'Đang tạo khóa mã hóa...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => 'Tiếp tục'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => 'Thiết bị không an toàn'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + 'Rất tiếc, chúng tôi không thể tạo khóa bảo mật trên thiết bị này.\n\nvui lòng đăng ký từ một thiết bị khác.'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => + 'Đã sao chép khóa khôi phục vào bộ nhớ tạm'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => 'Khóa khôi phục'; @override String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + 'Nếu bạn quên mật khẩu, cách duy nhất bạn có thể khôi phục dữ liệu của mình là sử dụng khóa này.'; @override String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + 'Chúng tôi không lưu trữ khóa này, vui lòng lưu khóa 24 từ này ở nơi an toàn.'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => 'Để sau'; @override - String get saveKey => 'Save key'; + String get saveKey => 'Lưu khóa'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => 'Đã lưu khoá dự phòng vào thư mục Tải về!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => 'Không có khóa khôi phục?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => 'Xác thực hai yếu tố'; @override String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + 'Nhập mã gồm 6 chữ số từ ứng dụng xác thực của bạn'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => 'Mất thiết bị?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => 'Nhập khóa khôi phục của bạn'; @override - String get recover => 'Recover'; + String get recover => 'Khôi phục'; + + @override + String get loggingOut => 'Đang đăng xuất...'; + + @override + String get immediately => 'Tức thì'; + + @override + String get appLock => 'Khóa ứng dụng'; + + @override + String get autoLock => 'Tự động khóa'; + + @override + String get noSystemLockFound => 'Không thấy khoá hệ thống'; + + @override + String get deviceLockEnablePreSteps => + 'Để bật khoá thiết bị, vui lòng thiết lập mật khẩu thiết bị hoặc khóa màn hình trong cài đặt hệ thống của bạn.'; + + @override + String get appLockDescription => + 'Chọn giữa màn hình khoá mặc định của thiết bị và màn hình khoá tự chọn dùng mã PIN hoặc mật khẩu.'; + + @override + String get deviceLock => 'Khóa thiết bị'; + + @override + String get pinLock => 'Mã PIN'; + + @override + String get autoLockFeatureDescription => + 'Thời gian ứng dụng tự khoá sau khi ở trạng thái nền'; + + @override + String get hideContent => 'Ẩn nội dung'; + + @override + String get hideContentDescriptionAndroid => + 'Ẩn nội dung khi chuyển ứng dụng và chặn chụp màn hình'; + + @override + String get hideContentDescriptioniOS => 'Ẩn nội dung khi chuyển ứng dụng'; + + @override + String get tooManyIncorrectAttempts => 'Quá nhiều lần thử không chính xác'; + + @override + String get tapToUnlock => 'Nhấn để mở khóa'; + + @override + String get areYouSureYouWantToLogout => 'Bạn có chắc chắn muốn đăng xuất?'; + + @override + String get yesLogout => 'Có, đăng xuất'; + + @override + String get authToViewSecrets => 'Vui lòng xác thực để xem bí mật của bạn'; + + @override + String get next => 'Tiếp theo'; + + @override + String get setNewPassword => 'Đặt lại mật khẩu'; + + @override + String get enterPin => 'Nhập mã PIN'; + + @override + String get setNewPin => 'Đổi mã PIN'; + + @override + String get confirm => 'Xác nhận'; + + @override + String get reEnterPassword => 'Nhập lại mật khẩu'; + + @override + String get reEnterPin => 'Nhập lại mã PIN'; + + @override + String get androidBiometricHint => 'Xác định danh tính'; + + @override + String get androidBiometricNotRecognized => + 'Không nhận dạng được. Vui lòng thử lại.'; + + @override + String get androidBiometricSuccess => 'Thành công'; + + @override + String get androidCancelButton => 'Hủy bỏ'; + + @override + String get androidSignInTitle => 'Yêu cầu xác thực'; + + @override + String get androidBiometricRequiredTitle => 'Yêu cầu sinh trắc học'; + + @override + String get androidDeviceCredentialsRequiredTitle => + 'Yêu cầu thông tin xác thực thiết bị'; + + @override + String get androidDeviceCredentialsSetupDescription => + 'Yêu cầu thông tin xác thực thiết bị'; + + @override + String get goToSettings => 'Chuyển đến cài đặt'; + + @override + String get androidGoToSettingsDescription => + 'Xác thực sinh trắc học chưa được thiết lập trên thiết bị của bạn. Đi tới \'Cài đặt > Bảo mật\' để thêm xác thực sinh trắc học.'; + + @override + String get iOSLockOut => + 'Xác thực sinh trắc học bị vô hiệu hóa. Vui lòng khóa và mở khóa màn hình của bạn để kích hoạt nó.'; + + @override + String get iOSOkButton => 'Đồng ý'; + + @override + String get emailAlreadyRegistered => 'Email đã được đăng kí.'; + + @override + String get emailNotRegistered => 'Email chưa được đăng kí.'; + + @override + String get thisEmailIsAlreadyInUse => 'Email này đã được sử dụng'; + + @override + String emailChangedTo(String newEmail) { + return 'Thay đổi email thành $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => + 'Xác thực lỗi, vui lòng thử lại'; + + @override + String get authenticationSuccessful => 'Xác thực thành công!'; + + @override + String get sessionExpired => 'Phiên làm việc đã hết hạn'; + + @override + String get incorrectRecoveryKey => 'Khóa khôi phục không chính xác'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => + 'Khóa khôi phục bạn đã nhập không chính xác'; + + @override + String get twofactorAuthenticationSuccessfullyReset => + 'Xác thực hai bước được khôi phục thành công'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => 'Mã xác minh của bạn đã hết hạn'; + + @override + String get incorrectCode => 'Mã không chính xác'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => + 'Xin lỗi, mã bạn đã nhập không chính xác'; + + @override + String get developerSettings => 'Cài đặt cho nhà phát triển'; + + @override + String get serverEndpoint => 'Điểm cuối máy chủ'; + + @override + String get invalidEndpoint => 'Điểm cuối không hợp lệ'; + + @override + String get invalidEndpointMessage => + 'Xin lỗi, điểm cuối bạn nhập không hợp lệ. Vui lòng nhập một điểm cuối hợp lệ và thử lại.'; + + @override + String get endpointUpdatedMessage => 'Cập nhật điểm cuối thành công'; } diff --git a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart index 6f476f999b..d0447c1877 100644 --- a/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart +++ b/mobile/packages/strings/lib/l10n/strings_localizations_zh.dart @@ -78,18 +78,6 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String get reportABug => '报告错误'; - @override - String get logsDirectoryName => 'logs'; - - @override - String get logsZipFileName => 'logs.zip'; - - @override - String get zipFileExtension => 'zip'; - - @override - String get reportABug => 'Report a bug'; - @override String get logsDialogBody => '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.'; @@ -99,7 +87,7 @@ class StringsLocalizationsZh extends StringsLocalizations { @override String customEndpoint(String endpoint) { - return 'Connected to $endpoint'; + return '已连接至 $endpoint'; } @override @@ -113,336 +101,1040 @@ class StringsLocalizationsZh extends StringsLocalizations { '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; @override - String get saveOnlyDescription => - 'Do you want to save this to your storage (Downloads folder by default)?'; + String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; @override String get enterNewEmailHint => 'Enter your new email address'; @override - String get email => 'Email'; + String get email => '电子邮件地址'; @override - String get verify => 'Verify'; + String get verify => '验证'; @override - String get invalidEmailTitle => 'Invalid email address'; + String get invalidEmailTitle => '无效的电子邮件地址'; @override - String get invalidEmailMessage => 'Please enter a valid email address.'; + String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; @override - String get pleaseWait => 'Please wait...'; + String get pleaseWait => '请稍候...'; @override - String get verifyPassword => 'Verify password'; + String get verifyPassword => '验证密码'; @override - String get incorrectPasswordTitle => 'Incorrect password'; + String get incorrectPasswordTitle => '密码错误'; @override - String get pleaseTryAgain => 'Please try again'; + String get pleaseTryAgain => '请重试'; @override - String get enterPassword => 'Enter password'; + String get enterPassword => '输入密码'; @override - String get enterYourPasswordHint => 'Enter your password'; + String get enterYourPasswordHint => '输入您的密码'; @override - String get activeSessions => 'Active sessions'; + String get activeSessions => '已登录的设备'; @override - String get oops => 'Oops'; + String get oops => '哎呀'; @override - String get somethingWentWrongPleaseTryAgain => - 'Something went wrong, please try again'; + String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; @override - String get thisWillLogYouOutOfThisDevice => - 'This will log you out of this device!'; + String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; @override - String get thisWillLogYouOutOfTheFollowingDevice => - 'This will log you out of the following device:'; + String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; @override - String get terminateSession => 'Terminate session?'; + String get terminateSession => '是否终止会话?'; @override - String get terminate => 'Terminate'; + String get terminate => '终止'; @override - String get thisDevice => 'This device'; + String get thisDevice => '此设备'; @override - String get createAccount => 'Create account'; + String get createAccount => '创建账户'; @override - String get weakStrength => 'Weak'; + String get weakStrength => '弱'; @override - String get moderateStrength => 'Moderate'; + String get moderateStrength => '中'; @override - String get strongStrength => 'Strong'; + String get strongStrength => '强'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => '删除账户'; @override - String get deleteAccountQuery => - 'We\'ll be sorry to see you go. Are you facing some issue?'; + String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; @override - String get yesSendFeedbackAction => 'Yes, send feedback'; + String get yesSendFeedbackAction => '是,发送反馈'; @override - String get noDeleteAccountAction => 'No, delete account'; + String get noDeleteAccountAction => '否,删除账户'; @override - String get initiateAccountDeleteTitle => - 'Please authenticate to initiate account deletion'; + String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; @override - String get confirmAccountDeleteTitle => 'Confirm account deletion'; + String get confirmAccountDeleteTitle => '确认删除账户'; @override String get confirmAccountDeleteMessage => - 'This account is linked to other Ente apps, if you use any.\n\nYour uploaded data, across all Ente apps, will be scheduled for deletion, and your account will be permanently deleted.'; + '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; @override - String get delete => 'Delete'; + String get delete => '删除'; @override - String get createNewAccount => 'Create new account'; + String get createNewAccount => '创建新账号'; @override - String get password => 'Password'; + String get password => '密码'; @override - String get confirmPassword => 'Confirm password'; + String get confirmPassword => '请确认密码'; @override String passwordStrength(String passwordStrengthValue) { - return 'Password strength: $passwordStrengthValue'; + return '密码强度: $passwordStrengthValue'; } @override - String get hearUsWhereTitle => 'How did you hear about Ente? (optional)'; + String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; @override - String get hearUsExplanation => - 'We don\'t track app installs. It\'d help if you told us where you found us!'; + String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; @override String get signUpTerms => - 'I agree to the terms of service and privacy policy'; + '我同意 服务条款隐私政策'; @override - String get termsOfServicesTitle => 'Terms'; + String get termsOfServicesTitle => '服务条款'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => '隐私政策'; @override String get ackPasswordLostWarning => - 'I understand that if I lose my password, I may lose my data since my data is end-to-end encrypted.'; + '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; @override - String get encryption => 'Encryption'; + String get encryption => '加密'; @override - String get logInLabel => 'Log in'; + String get logInLabel => '登录'; @override - String get welcomeBack => 'Welcome back!'; + String get welcomeBack => '欢迎回来!'; @override String get loginTerms => - 'By clicking log in, I agree to the terms of service and privacy policy'; + '点击登录后,我同意 服务条款隐私政策'; @override - String get noInternetConnection => 'No internet connection'; + String get noInternetConnection => '无互联网连接'; @override - String get pleaseCheckYourInternetConnectionAndTryAgain => - 'Please check your internet connection and try again.'; + String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; @override - String get verificationFailedPleaseTryAgain => - 'Verification failed, please try again'; + String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; @override - String get recreatePasswordTitle => 'Recreate password'; + String get recreatePasswordTitle => '重新创建密码'; @override String get recreatePasswordBody => - '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).'; + '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; @override - String get useRecoveryKey => 'Use recovery key'; + String get useRecoveryKey => '使用恢复密钥'; @override - String get forgotPassword => 'Forgot password'; + String get forgotPassword => '忘记密码'; @override - String get changeEmail => 'Change email'; + String get changeEmail => '修改邮箱'; @override - String get verifyEmail => 'Verify email'; + String get verifyEmail => '验证电子邮件'; @override String weHaveSendEmailTo(String email) { - return 'We have sent a mail to $email'; + return '我们已经发送邮件到 $email'; } @override - String get toResetVerifyEmail => - 'To reset your password, please verify your email first.'; + String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; @override - String get checkInboxAndSpamFolder => - 'Please check your inbox (and spam) to complete verification'; + String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; @override - String get tapToEnterCode => 'Tap to enter code'; + String get tapToEnterCode => '点击以输入代码'; @override - String get sendEmail => 'Send email'; + String get sendEmail => '发送电子邮件'; @override - String get resendEmail => 'Resend email'; + String get resendEmail => '重新发送电子邮件'; @override - String get passKeyPendingVerification => 'Verification is still pending'; + String get passKeyPendingVerification => '仍需验证'; @override - String get loginSessionExpired => 'Session expired'; + String get loginSessionExpired => '会话已过期'; @override - String get loginSessionExpiredDetails => - 'Your session has expired. Please login again.'; + String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; @override - String get passkeyAuthTitle => 'Passkey verification'; + String get passkeyAuthTitle => '通行密钥验证'; @override - String get waitingForVerification => 'Waiting for verification...'; + String get waitingForVerification => '等待验证...'; @override - String get tryAgain => 'Try again'; + String get tryAgain => '请再试一次'; @override - String get checkStatus => 'Check status'; + String get checkStatus => '检查状态'; @override - String get loginWithTOTP => 'Login with TOTP'; + String get loginWithTOTP => '使用 TOTP 登录'; @override - String get recoverAccount => 'Recover account'; + String get recoverAccount => '恢复账户'; @override - String get setPasswordTitle => 'Set password'; + String get setPasswordTitle => '设置密码'; @override - String get changePasswordTitle => 'Change password'; + String get changePasswordTitle => '修改密码'; @override - String get resetPasswordTitle => 'Reset password'; + String get resetPasswordTitle => '重置密码'; @override - String get encryptionKeys => 'Encryption keys'; + String get encryptionKeys => '加密密钥'; @override - String get enterPasswordToEncrypt => - 'Enter a password we can use to encrypt your data'; + String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; @override - String get enterNewPasswordToEncrypt => - 'Enter a new password we can use to encrypt your data'; + String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; @override String get passwordWarning => - 'We don\'t store this password, so if you forget, we cannot decrypt your data'; + '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; @override - String get howItWorks => 'How it works'; + String get howItWorks => '工作原理'; @override - String get generatingEncryptionKeys => 'Generating encryption keys...'; + String get generatingEncryptionKeys => '正在生成加密密钥...'; @override - String get passwordChangedSuccessfully => 'Password changed successfully'; + String get passwordChangedSuccessfully => '密码修改成功'; @override - String get signOutFromOtherDevices => 'Sign out from other devices'; + String get signOutFromOtherDevices => '从其他设备登出'; @override - String get signOutOtherBody => - 'If you think someone might know your password, you can force all other devices using your account to sign out.'; + String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; @override - String get signOutOtherDevices => 'Sign out other devices'; + String get signOutOtherDevices => '登出其他设备'; @override - String get doNotSignOut => 'Do not sign out'; + String get doNotSignOut => '不要登出'; @override - String get generatingEncryptionKeysTitle => 'Generating encryption keys...'; + String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; @override - String get continueLabel => 'Continue'; + String get continueLabel => '继续'; @override - String get insecureDevice => 'Insecure device'; + String get insecureDevice => '设备不安全'; @override String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => - 'Sorry, we could not generate secure keys on this device.\n\nplease sign up from a different device.'; + '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; @override - String get recoveryKeyCopiedToClipboard => 'Recovery key copied to clipboard'; + String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; @override - String get recoveryKey => 'Recovery key'; + String get recoveryKey => '恢复密钥'; @override - String get recoveryKeyOnForgotPassword => - 'If you forget your password, the only way you can recover your data is with this key.'; + String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; @override - String get recoveryKeySaveDescription => - 'We don\'t store this key, please save this 24 word key in a safe place.'; + String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; @override - String get doThisLater => 'Do this later'; + String get doThisLater => '稍后再做'; @override - String get saveKey => 'Save key'; + String get saveKey => '保存密钥'; @override - String get recoveryKeySaved => 'Recovery key saved in Downloads folder!'; + String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; @override - String get noRecoveryKeyTitle => 'No recovery key?'; + String get noRecoveryKeyTitle => '没有恢复密钥吗?'; @override - String get twoFactorAuthTitle => 'Two-factor authentication'; + String get twoFactorAuthTitle => '两步验证'; @override - String get enterCodeHint => - 'Enter the 6-digit code from\nyour authenticator app'; + String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; @override - String get lostDeviceTitle => 'Lost device?'; + String get lostDeviceTitle => '丢失了设备吗?'; @override - String get enterRecoveryKeyHint => 'Enter your recovery key'; + String get enterRecoveryKeyHint => '输入您的恢复密钥'; @override - String get recover => 'Recover'; + String get recover => '恢复'; + + @override + String get loggingOut => '正在登出...'; + + @override + String get immediately => '立即'; + + @override + String get appLock => '应用锁'; + + @override + String get autoLock => '自动锁定'; + + @override + String get noSystemLockFound => '未找到系统锁'; + + @override + String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; + + @override + String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; + + @override + String get deviceLock => '设备锁'; + + @override + String get pinLock => 'Pin 锁定'; + + @override + String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; + + @override + String get hideContent => '隐藏内容'; + + @override + String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; + + @override + String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; + + @override + String get tooManyIncorrectAttempts => '错误的尝试次数过多'; + + @override + String get tapToUnlock => '点击解锁'; + + @override + String get areYouSureYouWantToLogout => '您确定要登出吗?'; + + @override + String get yesLogout => '是的,登出'; + + @override + String get authToViewSecrets => '请进行身份验证以查看您的密钥'; + + @override + String get next => '下一步'; + + @override + String get setNewPassword => '设置新密码'; + + @override + String get enterPin => '输入 PIN 码'; + + @override + String get setNewPin => '设置新 PIN 码'; + + @override + String get confirm => '确认'; + + @override + String get reEnterPassword => '再次输入密码'; + + @override + String get reEnterPin => '再次输入 PIN 码'; + + @override + String get androidBiometricHint => '验证身份'; + + @override + String get androidBiometricNotRecognized => '未能识别,请重试。'; + + @override + String get androidBiometricSuccess => '成功'; + + @override + String get androidCancelButton => '取消'; + + @override + String get androidSignInTitle => '需要进行身份验证'; + + @override + String get androidBiometricRequiredTitle => '需要进行生物识别认证'; + + @override + String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; + + @override + String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; + + @override + String get goToSettings => '前往设置'; + + @override + String get androidGoToSettingsDescription => + '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; + + @override + String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; + + @override + String get iOSOkButton => '好'; + + @override + String get emailAlreadyRegistered => '电子邮件地址已被注册。'; + + @override + String get emailNotRegistered => '电子邮件地址未注册。'; + + @override + String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; + + @override + String emailChangedTo(String newEmail) { + return '电子邮件已更改为 $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; + + @override + String get authenticationSuccessful => '认证成功!'; + + @override + String get sessionExpired => '会话已过期'; + + @override + String get incorrectRecoveryKey => '恢复密钥不正确'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; + + @override + String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; + + @override + String get noRecoveryKey => 'No recovery key'; + + @override + String get yourAccountHasBeenDeleted => 'Your account has been deleted'; + + @override + String get verificationId => 'Verification ID'; + + @override + String get yourVerificationCodeHasExpired => '您的验证码已过期'; + + @override + String get incorrectCode => '验证码错误'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; + + @override + String get developerSettings => '开发者设置'; + + @override + String get serverEndpoint => '服务器端点'; + + @override + String get invalidEndpoint => '端点无效'; + + @override + String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; + + @override + String get endpointUpdatedMessage => '端点更新成功'; +} + +/// The translations for Chinese, as used in China (`zh_CN`). +class StringsLocalizationsZhCn extends StringsLocalizationsZh { + StringsLocalizationsZhCn() : super('zh_CN'); + + @override + String get networkHostLookUpErr => '无法连接到 Ente,请检查您的网络设置,如果错误仍然存​​在,请联系支持。'; + + @override + String get networkConnectionRefusedErr => + '无法连接到 Ente,请稍后重试。如果错误仍然存​​在,请联系支持人员。'; + + @override + String get itLooksLikeSomethingWentWrongPleaseRetryAfterSome => + '看起来出了点问题。 请稍后重试。 如果错误仍然存在,请联系我们的支持团队。'; + + @override + String get error => '错误'; + + @override + String get ok => '确定'; + + @override + String get faq => '常见问题'; + + @override + String get contactSupport => '联系支持'; + + @override + String get emailYourLogs => '通过电子邮件发送您的日志'; + + @override + String pleaseSendTheLogsTo(String toEmail) { + return '请将日志发送至 \n$toEmail'; + } + + @override + String get copyEmailAddress => '复制电子邮件地址'; + + @override + String get exportLogs => '导出日志'; + + @override + String get cancel => '取消'; + + @override + String get reportABug => '报告错误'; + + @override + String customEndpoint(String endpoint) { + return '已连接至 $endpoint'; + } + + @override + String get save => '保存'; + + @override + String get send => '发送'; + + @override + String get saveOrSendDescription => + '您想将其保存到您的内置存储(默认情况下为“下载”文件夹)还是将其发送到其他应用程序?'; + + @override + String get saveOnlyDescription => '您想将其保存到您的内置存储中(默认情况下为“下载”文件夹)吗?'; + + @override + String get enterNewEmailHint => '请输入您的新电子邮件地址'; + + @override + String get email => '电子邮件地址'; + + @override + String get verify => '验证'; + + @override + String get invalidEmailTitle => '无效的电子邮件地址'; + + @override + String get invalidEmailMessage => '请输入一个有效的电子邮件地址。'; + + @override + String get pleaseWait => '请稍候...'; + + @override + String get verifyPassword => '验证密码'; + + @override + String get incorrectPasswordTitle => '密码错误'; + + @override + String get pleaseTryAgain => '请重试'; + + @override + String get enterPassword => '输入密码'; + + @override + String get enterYourPasswordHint => '输入您的密码'; + + @override + String get activeSessions => '已登录的设备'; + + @override + String get oops => '哎呀'; + + @override + String get somethingWentWrongPleaseTryAgain => '出了点问题,请重试'; + + @override + String get thisWillLogYouOutOfThisDevice => '这将使您登出该设备!'; + + @override + String get thisWillLogYouOutOfTheFollowingDevice => '这将使您登出以下设备:'; + + @override + String get terminateSession => '是否终止会话?'; + + @override + String get terminate => '终止'; + + @override + String get thisDevice => '此设备'; + + @override + String get createAccount => '创建账户'; + + @override + String get weakStrength => '弱'; + + @override + String get moderateStrength => '中'; + + @override + String get strongStrength => '强'; + + @override + String get deleteAccount => '删除账户'; + + @override + String get deleteAccountQuery => '我们很抱歉看到您离开。您面临一些问题?'; + + @override + String get yesSendFeedbackAction => '是,发送反馈'; + + @override + String get noDeleteAccountAction => '否,删除账户'; + + @override + String get initiateAccountDeleteTitle => '请进行身份验证以启动账户删除'; + + @override + String get confirmAccountDeleteTitle => '确认删除账户'; + + @override + String get confirmAccountDeleteMessage => + '如果您使用其他 Ente 应用程序,该账户将会与其他应用程序链接。\n\n在所有 Ente 应用程序中,您上传的数据将被安排用于删除,并且您的账户将被永久删除。'; + + @override + String get delete => '删除'; + + @override + String get createNewAccount => '创建新账号'; + + @override + String get password => '密码'; + + @override + String get confirmPassword => '请确认密码'; + + @override + String passwordStrength(String passwordStrengthValue) { + return '密码强度: $passwordStrengthValue'; + } + + @override + String get hearUsWhereTitle => '您是怎么知道 Ente 的?(可选)'; + + @override + String get hearUsExplanation => '我们不跟踪应用程序安装情况。如果您告诉我们您是在哪里找到我们的,将会有所帮助!'; + + @override + String get signUpTerms => + '我同意 服务条款隐私政策'; + + @override + String get termsOfServicesTitle => '服务条款'; + + @override + String get privacyPolicyTitle => '隐私政策'; + + @override + String get ackPasswordLostWarning => + '我明白,如果我丢失密码,我可能会丢失我的数据,因为我的数据是 端到端加密的。'; + + @override + String get encryption => '加密'; + + @override + String get logInLabel => '登录'; + + @override + String get welcomeBack => '欢迎回来!'; + + @override + String get loginTerms => + '点击登录后,我同意 服务条款隐私政策'; + + @override + String get noInternetConnection => '无互联网连接'; + + @override + String get pleaseCheckYourInternetConnectionAndTryAgain => '请检查您的互联网连接,然后重试。'; + + @override + String get verificationFailedPleaseTryAgain => '验证失败,请再试一次'; + + @override + String get recreatePasswordTitle => '重新创建密码'; + + @override + String get recreatePasswordBody => + '当前设备的功能不足以验证您的密码,但我们可以以适用于所有设备的方式重新生成。\n\n请使用您的恢复密钥登录并重新生成您的密码(如果您愿意,可以再次使用相同的密码)。'; + + @override + String get useRecoveryKey => '使用恢复密钥'; + + @override + String get forgotPassword => '忘记密码'; + + @override + String get changeEmail => '修改邮箱'; + + @override + String get verifyEmail => '验证电子邮件'; + + @override + String weHaveSendEmailTo(String email) { + return '我们已经发送邮件到 $email'; + } + + @override + String get toResetVerifyEmail => '要重置您的密码,请先验证您的电子邮件。'; + + @override + String get checkInboxAndSpamFolder => '请检查您的收件箱 (或者是在您的“垃圾邮件”列表内) 以完成验证'; + + @override + String get tapToEnterCode => '点击以输入代码'; + + @override + String get sendEmail => '发送电子邮件'; + + @override + String get resendEmail => '重新发送电子邮件'; + + @override + String get passKeyPendingVerification => '仍需验证'; + + @override + String get loginSessionExpired => '会话已过期'; + + @override + String get loginSessionExpiredDetails => '您的会话已过期。请重新登录。'; + + @override + String get passkeyAuthTitle => '通行密钥验证'; + + @override + String get waitingForVerification => '等待验证...'; + + @override + String get tryAgain => '请再试一次'; + + @override + String get checkStatus => '检查状态'; + + @override + String get loginWithTOTP => '使用 TOTP 登录'; + + @override + String get recoverAccount => '恢复账户'; + + @override + String get setPasswordTitle => '设置密码'; + + @override + String get changePasswordTitle => '修改密码'; + + @override + String get resetPasswordTitle => '重置密码'; + + @override + String get encryptionKeys => '加密密钥'; + + @override + String get enterPasswordToEncrypt => '输入我们可以用来加密您的数据的密码'; + + @override + String get enterNewPasswordToEncrypt => '输入我们可以用来加密您的数据的新密码'; + + @override + String get passwordWarning => + '我们不储存这个密码,所以如果忘记, 我们不能解密您的数据'; + + @override + String get howItWorks => '工作原理'; + + @override + String get generatingEncryptionKeys => '正在生成加密密钥...'; + + @override + String get passwordChangedSuccessfully => '密码修改成功'; + + @override + String get signOutFromOtherDevices => '从其他设备登出'; + + @override + String get signOutOtherBody => '如果您认为有人可能知道您的密码,您可以强制所有其他使用您账户的设备登出。'; + + @override + String get signOutOtherDevices => '登出其他设备'; + + @override + String get doNotSignOut => '不要登出'; + + @override + String get generatingEncryptionKeysTitle => '正在生成加密密钥...'; + + @override + String get continueLabel => '继续'; + + @override + String get insecureDevice => '设备不安全'; + + @override + String get sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease => + '抱歉,我们无法在此设备上生成安全密钥。\n\n请使用其他设备注册。'; + + @override + String get recoveryKeyCopiedToClipboard => '恢复密钥已复制到剪贴板'; + + @override + String get recoveryKey => '恢复密钥'; + + @override + String get recoveryKeyOnForgotPassword => '如果您忘记了密码,恢复数据的唯一方法就是使用此密钥。'; + + @override + String get recoveryKeySaveDescription => '我们不会存储此密钥,请将此24个单词密钥保存在一个安全的地方。'; + + @override + String get doThisLater => '稍后再做'; + + @override + String get saveKey => '保存密钥'; + + @override + String get recoveryKeySaved => '恢复密钥已保存在下载文件夹中!'; + + @override + String get noRecoveryKeyTitle => '没有恢复密钥吗?'; + + @override + String get twoFactorAuthTitle => '两步验证'; + + @override + String get enterCodeHint => '从你的身份验证器应用中\n输入6位数字代码'; + + @override + String get lostDeviceTitle => '丢失了设备吗?'; + + @override + String get enterRecoveryKeyHint => '输入您的恢复密钥'; + + @override + String get recover => '恢复'; + + @override + String get loggingOut => '正在登出...'; + + @override + String get immediately => '立即'; + + @override + String get appLock => '应用锁'; + + @override + String get autoLock => '自动锁定'; + + @override + String get noSystemLockFound => '未找到系统锁'; + + @override + String get deviceLockEnablePreSteps => '要启用设备锁,请在系统设置中设置设备密码或屏幕锁。'; + + @override + String get appLockDescription => '在设备的默认锁定屏幕和带有 PIN 或密码的自定义锁定屏幕之间进行选择。'; + + @override + String get deviceLock => '设备锁'; + + @override + String get pinLock => 'Pin 锁定'; + + @override + String get autoLockFeatureDescription => '应用程序进入后台后锁定的时间'; + + @override + String get hideContent => '隐藏内容'; + + @override + String get hideContentDescriptionAndroid => '在应用切换器中隐藏应用内容并禁用屏幕截图'; + + @override + String get hideContentDescriptioniOS => '在应用切换器中隐藏应用内容'; + + @override + String get tooManyIncorrectAttempts => '错误的尝试次数过多'; + + @override + String get tapToUnlock => '点击解锁'; + + @override + String get areYouSureYouWantToLogout => '您确定要登出吗?'; + + @override + String get yesLogout => '是的,登出'; + + @override + String get authToViewSecrets => '请进行身份验证以查看您的密钥'; + + @override + String get next => '下一步'; + + @override + String get setNewPassword => '设置新密码'; + + @override + String get enterPin => '输入 PIN 码'; + + @override + String get setNewPin => '设置新 PIN 码'; + + @override + String get confirm => '确认'; + + @override + String get reEnterPassword => '再次输入密码'; + + @override + String get reEnterPin => '再次输入 PIN 码'; + + @override + String get androidBiometricHint => '验证身份'; + + @override + String get androidBiometricNotRecognized => '未能识别,请重试。'; + + @override + String get androidBiometricSuccess => '成功'; + + @override + String get androidCancelButton => '取消'; + + @override + String get androidSignInTitle => '需要进行身份验证'; + + @override + String get androidBiometricRequiredTitle => '需要进行生物识别认证'; + + @override + String get androidDeviceCredentialsRequiredTitle => '需要设备凭据'; + + @override + String get androidDeviceCredentialsSetupDescription => '需要设备凭据'; + + @override + String get goToSettings => '前往设置'; + + @override + String get androidGoToSettingsDescription => + '您的设备上未设置生物识别身份验证。转到“设置 > 安全”以添加生物识别身份验证。'; + + @override + String get iOSLockOut => '生物识别身份验证已禁用。请锁定并解锁屏幕以启用该功能。'; + + @override + String get iOSOkButton => '好'; + + @override + String get emailAlreadyRegistered => '电子邮件地址已被注册。'; + + @override + String get emailNotRegistered => '电子邮件地址未注册。'; + + @override + String get thisEmailIsAlreadyInUse => '该电子邮件已被使用'; + + @override + String emailChangedTo(String newEmail) { + return '电子邮件已更改为 $newEmail'; + } + + @override + String get authenticationFailedPleaseTryAgain => '认证失败,请重试'; + + @override + String get authenticationSuccessful => '认证成功!'; + + @override + String get sessionExpired => '会话已过期'; + + @override + String get incorrectRecoveryKey => '恢复密钥不正确'; + + @override + String get theRecoveryKeyYouEnteredIsIncorrect => '您输入的恢复密钥不正确'; + + @override + String get twofactorAuthenticationSuccessfullyReset => '两步验证已成功重置'; + + @override + String get yourVerificationCodeHasExpired => '您的验证码已过期'; + + @override + String get incorrectCode => '验证码错误'; + + @override + String get sorryTheCodeYouveEnteredIsIncorrect => '抱歉,您输入的验证码不正确'; + + @override + String get developerSettings => '开发者设置'; + + @override + String get serverEndpoint => '服务器端点'; + + @override + String get invalidEndpoint => '端点无效'; + + @override + String get invalidEndpointMessage => '抱歉,您输入的端点无效。请输入有效的端点,然后重试。'; + + @override + String get endpointUpdatedMessage => '端点更新成功'; } /// The translations for Chinese, as used in Taiwan (`zh_TW`). From ee035681ab44a9fb2b455bcdee44235101658f1d Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 15:40:14 +0530 Subject: [PATCH 122/164] Bump version to 4.4.3+444 --- mobile/apps/auth/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index ee8d176587..2a140877eb 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,7 +1,7 @@ name: ente_auth description: ente two-factor authenticator -version: 4.4.3+443 +version: 4.4.3+444 publish_to: none environment: From 210a0a45c1b9063c634a3e4b03bf8409d764c50a Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 6 Aug 2025 16:44:08 +0530 Subject: [PATCH 123/164] Bump version to 4.4.5+445 --- mobile/apps/auth/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 2a140877eb..a78cdb0377 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,7 +1,7 @@ name: ente_auth description: ente two-factor authenticator -version: 4.4.3+444 +version: 4.4.5+445 publish_to: none environment: From 085551b5a7f8ebf127bbce731fa691dd42271fb5 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 7 Aug 2025 19:39:58 +0530 Subject: [PATCH 124/164] Add ente_strings package and update localization delegates --- mobile/apps/auth/lib/app/view/app.dart | 3 +++ mobile/apps/auth/lib/main.dart | 6 ++++-- mobile/apps/auth/pubspec.lock | 2 +- mobile/apps/auth/pubspec.yaml | 2 ++ 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/mobile/apps/auth/lib/app/view/app.dart b/mobile/apps/auth/lib/app/view/app.dart index 81dedefa5f..ff4434f2e1 100644 --- a/mobile/apps/auth/lib/app/view/app.dart +++ b/mobile/apps/auth/lib/app/view/app.dart @@ -16,6 +16,7 @@ import 'package:ente_auth/ui/settings/app_update_dialog.dart'; import 'package:ente_events/event_bus.dart'; import 'package:ente_events/models/signed_in_event.dart'; import 'package:ente_events/models/signed_out_event.dart'; +import 'package:ente_strings/l10n/strings_localizations.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -131,6 +132,7 @@ class _AppState extends State localeListResolutionCallback: localResolutionCallBack, localizationsDelegates: const [ AppLocalizations.delegate, + StringsLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, @@ -150,6 +152,7 @@ class _AppState extends State localeListResolutionCallback: localResolutionCallBack, localizationsDelegates: const [ AppLocalizations.delegate, + StringsLocalizations.delegate, GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, GlobalWidgetsLocalizations.delegate, diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 5676f508eb..3f7d5f2fea 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -7,7 +7,7 @@ import "package:ente_auth/app/view/app.dart"; import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/core/constants.dart'; import 'package:ente_auth/ente_theme_data.dart'; -import 'package:ente_auth/l10n/l10n.dart'; +import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/locale.dart'; import 'package:ente_auth/services/authenticator_service.dart'; import 'package:ente_auth/services/billing_service.dart'; @@ -29,6 +29,7 @@ import 'package:ente_lock_screen/ui/app_lock.dart'; import 'package:ente_lock_screen/ui/lock_screen.dart'; import 'package:ente_logging/logging.dart'; import 'package:ente_network/network.dart'; +import 'package:ente_strings/l10n/strings_localizations.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_displaymode/flutter_displaymode.dart'; @@ -111,7 +112,8 @@ Future _runInForeground() async { darkTheme: darkThemeData, savedThemeMode: savedThemeMode, localeListResolutionCallback: localResolutionCallBack, - localizationsDelegates: const [ + localizationsDelegates: const [ + ...StringsLocalizations.localizationsDelegates, ...AppLocalizations.localizationsDelegates, ], supportedLocales: appSupportedLocales, diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 57852f892d..a0eb13e153 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -457,7 +457,7 @@ packages: source: path version: "1.0.0" ente_strings: - dependency: transitive + dependency: "direct main" description: path: "../../packages/strings" relative: true diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index a78cdb0377..5a67de92b6 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -42,6 +42,8 @@ dependencies: path: ../../packages/logging ente_network: path: ../../packages/network + ente_strings: + path: ../../packages/strings ente_ui: path: ../../packages/ui event_bus: ^2.0.0 From 9033bd8cec1584217fc3cca5e90311ef382357ea Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 7 Aug 2025 19:40:07 +0530 Subject: [PATCH 125/164] Fix formatting in custom-icons.json --- mobile/apps/auth/assets/custom-icons/_data/custom-icons.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json index 9301d65a2c..f9c13b07ed 100644 --- a/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json +++ b/mobile/apps/auth/assets/custom-icons/_data/custom-icons.json @@ -1730,7 +1730,7 @@ { "title": "Twitter", "altNames": [ - "X", + "X" ] }, { From e11a6ace80382a41ba3eec363fd16c54c5467b0b Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 7 Aug 2025 19:40:39 +0530 Subject: [PATCH 126/164] Refactor theme usage in dynamic FAB and dialog widget; improve imports and color scheme references --- mobile/apps/auth/lib/ui/account/logout_dialog.dart | 2 +- .../ui/lib/components/buttons/dynamic_fab.dart | 12 ++++-------- .../packages/ui/lib/components/dialog_widget.dart | 8 +++++--- mobile/packages/ui/lib/theme/ente_theme.dart | 14 ++++++-------- 4 files changed, 16 insertions(+), 20 deletions(-) diff --git a/mobile/apps/auth/lib/ui/account/logout_dialog.dart b/mobile/apps/auth/lib/ui/account/logout_dialog.dart index bb5defbe78..a2e7af6e62 100644 --- a/mobile/apps/auth/lib/ui/account/logout_dialog.dart +++ b/mobile/apps/auth/lib/ui/account/logout_dialog.dart @@ -1,7 +1,7 @@ import 'package:ente_auth/core/configuration.dart'; import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/store/authenticator_db.dart'; -import 'package:ente_auth/utils/dialog_util.dart'; +import 'package:ente_auth/utils/dialog_util.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; diff --git a/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart index 0cb31406dd..a6e9be9990 100644 --- a/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart +++ b/mobile/packages/ui/lib/components/buttons/dynamic_fab.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/theme/ente_theme_data.dart'; import 'package:flutter/material.dart'; class DynamicFAB extends StatelessWidget { @@ -24,7 +25,7 @@ class DynamicFAB extends StatelessWidget { decoration: BoxDecoration( boxShadow: [ BoxShadow( - color: getEnteColorScheme(context).alternativeColor, + color: Theme.of(context).colorScheme.surface, spreadRadius: 200, blurRadius: 100, offset: const Offset(0, 230), @@ -38,7 +39,7 @@ class DynamicFAB extends StatelessWidget { FloatingActionButton( heroTag: 'FAB', backgroundColor: - getEnteColorScheme(context).dynamicFABBackgroundColor, + Theme.of(context).colorScheme.dynamicFABBackgroundColor, foregroundColor: getEnteColorScheme(context).dynamicFABTextColor, onPressed: isFormValid! ? onPressedFunction as void Function()? @@ -64,12 +65,7 @@ class DynamicFAB extends StatelessWidget { child: OutlinedButton( onPressed: isFormValid! ? onPressedFunction as void Function()? : null, - child: Text( - buttonText!, - style: isFormValid! - ? getEnteTextTheme(context).body - : getEnteTextTheme(context).bodyFaint, - ), + child: Text(buttonText!), ), ); } diff --git a/mobile/packages/ui/lib/components/dialog_widget.dart b/mobile/packages/ui/lib/components/dialog_widget.dart index 53be98c476..36d849a719 100644 --- a/mobile/packages/ui/lib/components/dialog_widget.dart +++ b/mobile/packages/ui/lib/components/dialog_widget.dart @@ -8,6 +8,8 @@ import 'package:ente_ui/components/buttons/models/button_type.dart'; import 'package:ente_ui/components/components_constants.dart'; import 'package:ente_ui/components/separators.dart'; import 'package:ente_ui/components/text_input_widget.dart'; +import "package:ente_ui/theme/colors.dart"; +import "package:ente_ui/theme/effects.dart"; import 'package:ente_ui/theme/ente_theme.dart'; import 'package:flutter/material.dart'; @@ -24,7 +26,7 @@ Future showDialogWidget({ return showDialog( useRootNavigator: useRootNavigator, barrierDismissible: isDismissible, - barrierColor: getEnteColorScheme(context).backdropFaint, + barrierColor: backdropFaintDark, context: context, builder: (context) { final widthOfScreen = MediaQuery.of(context).size.width; @@ -70,7 +72,7 @@ class DialogWidget extends StatelessWidget { : const EdgeInsets.fromLTRB(6, 8, 6, 6), decoration: BoxDecoration( color: colorScheme.backgroundElevated, - boxShadow: getEnteShadowFloat(context), + boxShadow: shadowFloatLight, borderRadius: const BorderRadius.all(Radius.circular(8)), ), child: Material( @@ -225,7 +227,7 @@ class _TextInputDialogState extends State { : const EdgeInsets.fromLTRB(6, 8, 6, 6), decoration: BoxDecoration( color: colorScheme.backgroundElevated, - boxShadow: getEnteShadowFloat(context), + boxShadow: shadowFloatLight, borderRadius: const BorderRadius.all(Radius.circular(8)), ), child: Padding( diff --git a/mobile/packages/ui/lib/theme/ente_theme.dart b/mobile/packages/ui/lib/theme/ente_theme.dart index b8825bb70b..1fac82d428 100644 --- a/mobile/packages/ui/lib/theme/ente_theme.dart +++ b/mobile/packages/ui/lib/theme/ente_theme.dart @@ -1,5 +1,6 @@ import 'package:ente_ui/theme/colors.dart'; import 'package:ente_ui/theme/effects.dart'; +import "package:ente_ui/theme/ente_theme_data.dart"; import 'package:ente_ui/theme/text_style.dart'; import 'package:flutter/material.dart'; @@ -50,20 +51,17 @@ EnteColorScheme getEnteColorScheme( // Fallback to old system if new system is not available return inverse - ? getEnteColorScheme(context).inverseEnteTheme.colorScheme - : getEnteColorScheme(context); + ? Theme.of(context).colorScheme.inverseEnteTheme.colorScheme + : Theme.of(context).colorScheme.enteTheme.colorScheme; } EnteTextTheme getEnteTextTheme( BuildContext context, { bool inverse = false, }) { - final isDark = Theme.of(context).brightness == Brightness.dark; - if (inverse) { - return isDark ? lightTextTheme : darkTextTheme; - } else { - return isDark ? darkTextTheme : lightTextTheme; - } + return inverse + ? Theme.of(context).colorScheme.inverseEnteTheme.textTheme + : Theme.of(context).colorScheme.enteTheme.textTheme; } /// Get theme-aware shadow for floating elements (dialogs, modals, etc.) From 193e1374e18a1381591585ce1a89dc3c3b3c4045 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 7 Aug 2025 20:01:12 +0530 Subject: [PATCH 127/164] Refactor GradientButton to use ClipRRect --- .../components/buttons/gradient_button.dart | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/mobile/packages/ui/lib/components/buttons/gradient_button.dart b/mobile/packages/ui/lib/components/buttons/gradient_button.dart index b98c7894ef..f5b6d9eeb9 100644 --- a/mobile/packages/ui/lib/components/buttons/gradient_button.dart +++ b/mobile/packages/ui/lib/components/buttons/gradient_button.dart @@ -59,22 +59,24 @@ class GradientButton extends StatelessWidget { } return InkWell( onTap: onTap as void Function()?, - child: Container( - height: 56, - decoration: BoxDecoration( - gradient: LinearGradient( - begin: const Alignment(0.1, -0.9), - end: const Alignment(-0.6, 0.9), - colors: onTap != null - ? getEnteColorScheme(context).gradientButtonBgColors - : [ - getEnteColorScheme(context).fillMuted, - getEnteColorScheme(context).fillMuted, - ], + child: ClipRRect( + borderRadius: BorderRadius.circular(4), + child: Container( + height: 56, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: const Alignment(0.1, -0.9), + end: const Alignment(-0.6, 0.9), + colors: onTap != null + ? getEnteColorScheme(context).gradientButtonBgColors + : [ + getEnteColorScheme(context).fillMuted, + getEnteColorScheme(context).fillMuted, + ], + ), ), - borderRadius: BorderRadius.circular(24), + child: Center(child: buttonContent), ), - child: Center(child: buttonContent), ), ); } From 6fe89fdc0e5e2525897a54b52fb44c7dca5ddbf5 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 12:05:08 +0530 Subject: [PATCH 128/164] Add theme configuration and update color scheme in recovery key and lock screen pages --- mobile/apps/auth/lib/main.dart | 2 + .../accounts/lib/pages/recovery_key_page.dart | 4 +- .../lib/ui/lock_screen_confirm_pin.dart | 2 +- .../lock_screen/lib/ui/lock_screen_pin.dart | 2 +- mobile/packages/ui/lib/theme/colors.dart | 49 +++++++++++++++++++ mobile/packages/ui/lib/theme/ente_theme.dart | 11 +++-- .../packages/ui/lib/theme/theme_config.dart | 14 ++++++ 7 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 mobile/packages/ui/lib/theme/theme_config.dart diff --git a/mobile/apps/auth/lib/main.dart b/mobile/apps/auth/lib/main.dart index 3f7d5f2fea..100ac405c1 100644 --- a/mobile/apps/auth/lib/main.dart +++ b/mobile/apps/auth/lib/main.dart @@ -30,6 +30,7 @@ import 'package:ente_lock_screen/ui/lock_screen.dart'; import 'package:ente_logging/logging.dart'; import 'package:ente_network/network.dart'; import 'package:ente_strings/l10n/strings_localizations.dart'; +import 'package:ente_ui/theme/theme_config.dart'; import 'package:flutter/foundation.dart'; import "package:flutter/material.dart"; import 'package:flutter_displaymode/flutter_displaymode.dart'; @@ -90,6 +91,7 @@ void main() async { } Future _runInForeground() async { + AppThemeConfig.initialize(EnteApp.auth); final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode()); final configuration = Configuration.instance; return await _runWithLogs(() async { diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index a4ad8cb4aa..00f083d4c5 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -149,7 +149,7 @@ class _RecoveryKeyPageState extends State { begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ - getEnteColorScheme(context).primary500, + getEnteColorScheme(context).primary700, getEnteColorScheme(context).primary300, ], stops: const [0.0, 0.9753], @@ -248,7 +248,7 @@ class _RecoveryKeyPageState extends State { childrens.add( SizedBox( height: 56, - child: OutlinedButton( + child: ElevatedButton( onPressed: () async { await _saveKeys(); }, diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart index ecb4418968..e162c50bd2 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_confirm_pin.dart @@ -86,7 +86,7 @@ class _LockScreenConfirmPinState extends State { width: 48, padding: const EdgeInsets.only(top: 6.0), decoration: BoxDecoration( - border: Border.all(color: colorTheme.primary500), + border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)), borderRadius: BorderRadius.circular(15.0), ), ); diff --git a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart index 603ebdd67d..81f9f0a484 100644 --- a/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart +++ b/mobile/packages/lock_screen/lib/ui/lock_screen_pin.dart @@ -130,7 +130,7 @@ class _LockScreenPinState extends State { width: 48, padding: const EdgeInsets.only(top: 6.0), decoration: BoxDecoration( - border: Border.all(color: colorTheme.primary500), + border: Border.all(color: const Color.fromRGBO(45, 194, 98, 1.0)), borderRadius: BorderRadius.circular(15.0), ), ); diff --git a/mobile/packages/ui/lib/theme/colors.dart b/mobile/packages/ui/lib/theme/colors.dart index 17a641037f..ac4bc24138 100644 --- a/mobile/packages/ui/lib/theme/colors.dart +++ b/mobile/packages/ui/lib/theme/colors.dart @@ -1,3 +1,4 @@ +import "package:ente_ui/theme/theme_config.dart"; import 'package:flutter/material.dart'; /// This color scheme provides all the colors needed for a modern Flutter app, @@ -24,6 +25,54 @@ import 'package:flutter/material.dart'; /// ); /// ``` class EnteColorScheme extends ThemeExtension { + factory EnteColorScheme.fromApp( + EnteApp app, { + Brightness brightness = Brightness.light, + }) { + final appColors = switch (app) { + EnteApp.auth => ( + primary700: const Color.fromARGB(255, 164, 0, 182), + primary500: const Color.fromARGB(255, 204, 10, 101), + primary400: const Color.fromARGB(255, 122, 41, 193), + primary300: const Color.fromARGB(255, 152, 77, 244), + gradientButtonBgColor: const Color(0xFF531DAB), + gradientButtonBgColors: const [ + Color.fromARGB(255, 122, 41, 193), + Color.fromARGB(255, 122, 41, 193), + ], + ), + EnteApp.locker => ( + primary700: const Color.fromARGB(255, 0, 122, 255), + primary400: const Color.fromARGB(255, 52, 152, 255), + primary500: const Color.fromARGB(255, 102, 178, 255), + primary300: const Color.fromARGB(255, 153, 204, 255), + gradientButtonBgColor: const Color.fromRGBO(0, 122, 255, 1), + gradientButtonBgColors: const [ + Color.fromRGBO(0, 122, 255, 1), + Color.fromRGBO(52, 152, 255, 1), + ], + ), + }; + + return brightness == Brightness.light + ? EnteColorScheme.light( + primary700: appColors.primary700, + primary500: appColors.primary500, + primary400: appColors.primary400, + primary300: appColors.primary300, + gradientButtonBgColor: appColors.gradientButtonBgColor, + gradientButtonBgColors: appColors.gradientButtonBgColors, + ) + : EnteColorScheme.dark( + primary700: appColors.primary700, + primary500: appColors.primary500, + primary400: appColors.primary400, + primary300: appColors.primary300, + gradientButtonBgColor: appColors.gradientButtonBgColor, + gradientButtonBgColors: appColors.gradientButtonBgColors, + ); + } + // Background Colors final Color backgroundBase; final Color backgroundElevated; diff --git a/mobile/packages/ui/lib/theme/ente_theme.dart b/mobile/packages/ui/lib/theme/ente_theme.dart index 1fac82d428..3fb07b8022 100644 --- a/mobile/packages/ui/lib/theme/ente_theme.dart +++ b/mobile/packages/ui/lib/theme/ente_theme.dart @@ -2,6 +2,7 @@ import 'package:ente_ui/theme/colors.dart'; import 'package:ente_ui/theme/effects.dart'; import "package:ente_ui/theme/ente_theme_data.dart"; import 'package:ente_ui/theme/text_style.dart'; +import "package:ente_ui/theme/theme_config.dart"; import 'package:flutter/material.dart'; class EnteTheme { @@ -49,10 +50,12 @@ EnteColorScheme getEnteColorScheme( return colorScheme; } - // Fallback to old system if new system is not available - return inverse - ? Theme.of(context).colorScheme.inverseEnteTheme.colorScheme - : Theme.of(context).colorScheme.enteTheme.colorScheme; + final brightness = Theme.of(context).brightness; + + return EnteColorScheme.fromApp( + AppThemeConfig.currentApp, + brightness: brightness, + ); } EnteTextTheme getEnteTextTheme( diff --git a/mobile/packages/ui/lib/theme/theme_config.dart b/mobile/packages/ui/lib/theme/theme_config.dart new file mode 100644 index 0000000000..a008370504 --- /dev/null +++ b/mobile/packages/ui/lib/theme/theme_config.dart @@ -0,0 +1,14 @@ +enum EnteApp { + auth, + locker; +} + +class AppThemeConfig { + static EnteApp? _currentApp; + + static void initialize(EnteApp app) { + _currentApp = app; + } + + static EnteApp get currentApp => _currentApp ?? EnteApp.auth; +} From ea409fc2665ed098daabeaf6b80fdeb553041f91 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 12:16:47 +0530 Subject: [PATCH 129/164] Bump version to 4.4.6+445 --- mobile/apps/auth/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 5a67de92b6..b1f73e7bab 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,7 +1,7 @@ name: ente_auth description: ente two-factor authenticator -version: 4.4.5+445 +version: 4.4.6+445 publish_to: none environment: From a5a19581fc83e2d0930dd412e9cee664b1d8b193 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 12:39:12 +0530 Subject: [PATCH 130/164] Revert "Bump version to 4.4.6+445" This reverts commit ea409fc2665ed098daabeaf6b80fdeb553041f91. --- mobile/apps/auth/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index b1f73e7bab..5a67de92b6 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,7 +1,7 @@ name: ente_auth description: ente two-factor authenticator -version: 4.4.6+445 +version: 4.4.5+445 publish_to: none environment: From 94e398dc89b2023b18b12bc9bc4cf4f127505a86 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 12:40:30 +0530 Subject: [PATCH 131/164] Bump version 4.4.6+446 --- mobile/apps/auth/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 5a67de92b6..ab04fdce34 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,7 +1,7 @@ name: ente_auth description: ente two-factor authenticator -version: 4.4.5+445 +version: 4.4.6+446 publish_to: none environment: From 6bd5327d50939fa4f8896b2d071429cc0d6656c9 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Fri, 8 Aug 2025 20:00:14 +0530 Subject: [PATCH 132/164] refactor: show black color as default background color in hideAppContent --- mobile/apps/auth/lib/utils/lock_screen_settings.dart | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mobile/apps/auth/lib/utils/lock_screen_settings.dart b/mobile/apps/auth/lib/utils/lock_screen_settings.dart index 247f7ae4b2..5831efc7e8 100644 --- a/mobile/apps/auth/lib/utils/lock_screen_settings.dart +++ b/mobile/apps/auth/lib/utils/lock_screen_settings.dart @@ -93,7 +93,6 @@ class LockScreenSettings { Future setHideAppContent(bool hideContent) async { if (PlatformUtil.isDesktop()) return; - final bool isLightMode = _preferences.getBool(kIsLightMode) ?? true; !hideContent ? PrivacyScreen.instance.disable() : await PrivacyScreen.instance.enable( @@ -103,11 +102,8 @@ class LockScreenSettings { androidOptions: const PrivacyAndroidOptions( enableSecure: true, ), - backgroundColor: - isLightMode ? const Color(0xffffffff) : const Color(0xff000000), - blurEffect: isLightMode - ? PrivacyBlurEffect.extraLight - : PrivacyBlurEffect.extraLight, + backgroundColor: const Color(0xff000000), + blurEffect: PrivacyBlurEffect.extraLight, ); await _preferences.setBool(keyHideAppContent, hideContent); } From a74908214d61caf18e7a6b4f6dd8b2289568dd62 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Mon, 11 Aug 2025 10:38:44 +0530 Subject: [PATCH 133/164] Update password entry page to use a fixed color for valid field value --- .../accounts/lib/pages/password_entry_page.dart | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/mobile/packages/accounts/lib/pages/password_entry_page.dart b/mobile/packages/accounts/lib/pages/password_entry_page.dart index 36152d883b..85b6f63b25 100644 --- a/mobile/packages/accounts/lib/pages/password_entry_page.dart +++ b/mobile/packages/accounts/lib/pages/password_entry_page.dart @@ -87,10 +87,7 @@ class _PasswordEntryPageState extends State { @override Widget build(BuildContext context) { final isKeypadOpen = MediaQuery.of(context).viewInsets.bottom > 100; - - // Initialize theme-aware color - final colorScheme = getEnteColorScheme(context); - _validFieldValueColor = colorScheme.fillFaint.withOpacity(0.5); + _validFieldValueColor = const Color.fromRGBO(45, 194, 98, 0.2); FloatingActionButtonLocation? fabLocation() { if (isKeypadOpen) { @@ -232,8 +229,9 @@ class _PasswordEntryPageState extends State { null); }, decoration: InputDecoration( - fillColor: - _isPasswordValid ? _validFieldValueColor : null, + fillColor: _isPasswordValid + ? _validFieldValueColor + : getEnteColorScheme(context).fillFaint, filled: true, hintText: context.strings.password, contentPadding: const EdgeInsets.all(20), @@ -295,8 +293,9 @@ class _PasswordEntryPageState extends State { onEditingComplete: () => TextInput.finishAutofillContext(), decoration: InputDecoration( - fillColor: - _passwordsMatch ? _validFieldValueColor : null, + fillColor: _passwordsMatch + ? _validFieldValueColor + : getEnteColorScheme(context).fillFaint, filled: true, hintText: context.strings.confirmPassword, contentPadding: const EdgeInsets.symmetric( From 8c5f7e62bef4d8fa9462c071b15fd2a161cb9d55 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Wed, 13 Aug 2025 10:55:23 +0530 Subject: [PATCH 134/164] Don't log clip queries --- .../semantic_search_service.dart | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart index cf3930a782..71ab953b6d 100644 --- a/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart +++ b/mobile/apps/photos/lib/services/machine_learning/semantic_search/semantic_search_service.dart @@ -1,6 +1,4 @@ import "dart:async" show Timer, unawaited; -import "dart:developer" as dev show log; -import "dart:math" show min; import "dart:ui" show Image; import "package:flutter/foundation.dart"; @@ -149,11 +147,13 @@ class SemanticSearchService { }, ); final queryResults = similarityResults[query]!; - // print query for top ten scores - for (int i = 0; i < min(10, queryResults.length); i++) { - final result = queryResults[i]; - dev.log("Query: $query, Score: ${result.score}, index $i"); - } + // Uncomment if needed for debugging: print query for top ten scores + // if (kDebugMode) { + // for (int i = 0; i < min(10, queryResults.length); i++) { + // final result = queryResults[i]; + // dev.log("Query: $query, Score: ${result.score}, index $i"); + // } + // } final Map fileIDToScoreMap = {}; for (final result in queryResults) { @@ -265,6 +265,13 @@ class SemanticSearchService { required Map minimumSimilarityMap, }) async { final startTime = DateTime.now(); + // Uncomment if needed for debugging: print query embeddings + // if (kDebugMode) { + // for (final queryText in textQueryToEmbeddingMap.keys) { + // final embedding = textQueryToEmbeddingMap[queryText]!; + // dev.log("CLIPTEXT Query: $queryText, embedding: $embedding"); + // } + // } await _cacheClipVectors(); final Map> queryResults = await MLComputer .instance From 5fe86858ef360d879b1cef50cf372f02d43bf2c1 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Wed, 13 Aug 2025 11:38:27 +0530 Subject: [PATCH 135/164] Update dependencies and upgrade SDK version; replace qr_code_scanner with qr_code_scanner_plus --- mobile/apps/auth/android/app/build.gradle | 2 +- mobile/apps/auth/lib/ui/scanner_gauth_page.dart | 2 +- mobile/apps/auth/lib/ui/scanner_page.dart | 2 +- mobile/apps/auth/pubspec.lock | 17 +++++++++-------- mobile/apps/auth/pubspec.yaml | 7 +++++-- mobile/packages/accounts/pubspec.lock | 16 ++++++++++++---- mobile/packages/lock_screen/pubspec.lock | 16 ++++++++++++---- mobile/packages/ui/pubspec.lock | 16 ++++++++++++---- mobile/packages/utils/lib/platform_util.dart | 4 ++-- mobile/packages/utils/pubspec.lock | 4 ++-- mobile/packages/utils/pubspec.yaml | 2 +- 11 files changed, 58 insertions(+), 30 deletions(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index 930690df6c..5cf5b50776 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -30,7 +30,7 @@ if (keystorePropertiesFile.exists()) { android { namespace "io.ente.auth" - compileSdk 34 + compileSdk 35 ndkVersion flutter.ndkVersion compileOptions { diff --git a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart index cb2bb716f9..58e6da3b41 100644 --- a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart @@ -6,7 +6,7 @@ import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/settings/data/import/google_auth_import.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; class ScannerGoogleAuthPage extends StatefulWidget { const ScannerGoogleAuthPage({super.key}); diff --git a/mobile/apps/auth/lib/ui/scanner_page.dart b/mobile/apps/auth/lib/ui/scanner_page.dart index 877920390e..65eabf6d3a 100644 --- a/mobile/apps/auth/lib/ui/scanner_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_page.dart @@ -4,7 +4,7 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner/qr_code_scanner.dart'; +import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; class ScannerPage extends StatefulWidget { const ScannerPage({super.key}); diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 46f3f4b5d7..84bc01d3b9 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -1144,10 +1144,11 @@ packages: move_to_background: dependency: "direct main" description: - name: move_to_background - sha256: "00caad17a6ce149910777131503f43f8ed80025681f94684e3a6a87d979b914c" - url: "https://pub.dev" - source: hosted + path: "." + ref: v2-only + resolved-ref: "0cdfeed654d79636eff0c57110f3f6ad5801ba2f" + url: "https://github.com/ente-io/move_to_background.git" + source: git version: "1.0.2" native_dio_adapter: dependency: "direct main" @@ -1389,14 +1390,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - qr_code_scanner: + qr_code_scanner_plus: dependency: "direct main" description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd + name: qr_code_scanner_plus + sha256: "39696b50d277097ee4d90d4292de36f38c66213a4f5216a06b2bdd2b63117859" url: "https://pub.dev" source: hosted - version: "1.0.1" + version: "2.0.10+1" qr_flutter: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index fde10d2539..04f45cafeb 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -91,7 +91,10 @@ dependencies: local_auth_darwin: ^1.2.2 logging: ^1.0.1 modal_bottom_sheet: ^3.0.0 - move_to_background: ^1.0.2 + move_to_background: # no updates in git, replace package + git: + url: https://github.com/ente-io/move_to_background.git + ref: v2-only native_dio_adapter: ^1.4.0 otp: ^3.1.1 package_info_plus: ^8.0.2 @@ -102,7 +105,7 @@ dependencies: pointycastle: ^3.7.3 privacy_screen: ^0.0.6 protobuf: ^4.1.0 - qr_code_scanner: ^1.0.1 + qr_code_scanner_plus: ^2.0.10+1 qr_flutter: ^4.1.0 sentry: ^8.14.2 sentry_flutter: ^8.14.2 diff --git a/mobile/packages/accounts/pubspec.lock b/mobile/packages/accounts/pubspec.lock index 760f4486a9..641615d91f 100644 --- a/mobile/packages/accounts/pubspec.lock +++ b/mobile/packages/accounts/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: @@ -338,10 +338,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "7.0.0" flutter_inappwebview: dependency: transitive description: @@ -854,6 +854,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" privacy_screen: dependency: transitive description: diff --git a/mobile/packages/lock_screen/pubspec.lock b/mobile/packages/lock_screen/pubspec.lock index d9b7176341..4b6d02e6f3 100644 --- a/mobile/packages/lock_screen/pubspec.lock +++ b/mobile/packages/lock_screen/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: @@ -338,10 +338,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "7.0.0" flutter_inappwebview: dependency: transitive description: @@ -854,6 +854,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" privacy_screen: dependency: "direct main" description: diff --git a/mobile/packages/ui/pubspec.lock b/mobile/packages/ui/pubspec.lock index 83cd670f3c..ba4200875e 100644 --- a/mobile/packages/ui/pubspec.lock +++ b/mobile/packages/ui/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" args: dependency: transitive description: @@ -253,10 +253,10 @@ packages: dependency: transitive description: name: flutter_email_sender - sha256: fb515d4e073d238d0daf1d765e5318487b6396d46b96e0ae9745dbc9a133f97a + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "7.0.0" flutter_inappwebview: dependency: "direct main" description: @@ -640,6 +640,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" + url: "https://pub.dev" + source: hosted + version: "6.0.3" screen_retriever: dependency: transitive description: diff --git a/mobile/packages/utils/lib/platform_util.dart b/mobile/packages/utils/lib/platform_util.dart index 6ad91b0d88..e84349c301 100644 --- a/mobile/packages/utils/lib/platform_util.dart +++ b/mobile/packages/utils/lib/platform_util.dart @@ -43,14 +43,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - fileExtension: extension, + ext: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - fileExtension: extension, + ext: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index 154ea69750..c5ec3f8a8a 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -232,10 +232,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + sha256: "448b1e30142cffe52f37ee085ea9ca50670d5425bb09b649d193549b2dcf6e26" url: "https://pub.dev" source: hosted - version: "0.3.1" + version: "0.3.0" fixnum: dependency: transitive description: diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index f996aae953..42af64e151 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: path: ../../packages/strings ente_ui: path: ../../packages/ui - file_saver: ^0.3.0 + file_saver: 0.3.0 flutter: sdk: flutter flutter_email_sender: ^7.0.0 From 72ede1a1097445ab3be1053fc3a18d187429e3ee Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 14 Aug 2025 00:09:48 +0530 Subject: [PATCH 136/164] Add onLongPress callback to MenuItemWidget --- mobile/packages/ui/lib/components/menu_item_widget.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/packages/ui/lib/components/menu_item_widget.dart b/mobile/packages/ui/lib/components/menu_item_widget.dart index 892b846493..31ce408331 100644 --- a/mobile/packages/ui/lib/components/menu_item_widget.dart +++ b/mobile/packages/ui/lib/components/menu_item_widget.dart @@ -31,6 +31,7 @@ class MenuItemWidget extends StatefulWidget { final double trailingExtraMargin; final FutureVoidCallback? onTap; final VoidCallback? onDoubleTap; + final VoidCallback? onLongPress; final Color? menuItemColor; final bool alignCaptionedTextToLeft; @@ -74,6 +75,7 @@ class MenuItemWidget extends StatefulWidget { this.trailingExtraMargin = 0.0, this.onTap, this.onDoubleTap, + this.onLongPress, this.menuItemColor, this.alignCaptionedTextToLeft = false, this.singleBorderRadius = 4.0, @@ -144,6 +146,7 @@ class _MenuItemWidgetState extends State { : GestureDetector( onTap: _onTap, onDoubleTap: widget.onDoubleTap, + onLongPress: widget.onLongPress, onTapDown: _onTapDown, onTapUp: _onTapUp, onTapCancel: _onCancel, @@ -264,7 +267,9 @@ class _MenuItemWidgetState extends State { } bool hasPassedGestureCallbacks() { - return widget.onDoubleTap != null || widget.onTap != null; + return widget.onDoubleTap != null || + widget.onTap != null || + widget.onLongPress != null; } void _onTapUp(details) { From b91759290107d4e4b2ed04b67820df63a60e1545 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:47:30 +0530 Subject: [PATCH 137/164] specify rust version --- .github/workflows/mobile-daily-internal.yml | 1 + .github/workflows/mobile-internal-release.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/mobile-daily-internal.yml b/.github/workflows/mobile-daily-internal.yml index 0429b8c001..2dee4c4ef4 100644 --- a/.github/workflows/mobile-daily-internal.yml +++ b/.github/workflows/mobile-daily-internal.yml @@ -8,6 +8,7 @@ on: env: FLUTTER_VERSION: "3.32.8" + RUST_VERSION: "1.85.1" permissions: contents: read diff --git a/.github/workflows/mobile-internal-release.yml b/.github/workflows/mobile-internal-release.yml index 43b2124cb8..768609dfa3 100644 --- a/.github/workflows/mobile-internal-release.yml +++ b/.github/workflows/mobile-internal-release.yml @@ -5,6 +5,7 @@ on: env: FLUTTER_VERSION: "3.32.8" + RUST_VERSION: "1.85.1" permissions: contents: write From 9b23ec59537bd3a203915c08f69a795ed56e2b23 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 13:55:34 +0530 Subject: [PATCH 138/164] No dependency on rust version specifically --- mobile/apps/photos/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/apps/photos/README.md b/mobile/apps/photos/README.md index 4b5f015838..8558c8ba30 100644 --- a/mobile/apps/photos/README.md +++ b/mobile/apps/photos/README.md @@ -46,7 +46,7 @@ You can alternatively install the build from PlayStore or F-Droid. ## 🧑‍💻 Building from source -1. Install [Flutter v3.32.8](https://flutter.dev/docs/get-started/install) and [Rust v1.85.1](https://www.rust-lang.org/tools/install). +1. Install [Flutter v3.32.8](https://flutter.dev/docs/get-started/install) and [Rust](https://www.rust-lang.org/tools/install). 2. Install [Flutter Rust Bridge](https://cjycode.com/flutter_rust_bridge/) with `cargo install flutter_rust_bridge_codegen` From 00b9d277d2535016d4412560a1d6b94284fe88d3 Mon Sep 17 00:00:00 2001 From: laurenspriem Date: Thu, 14 Aug 2025 17:29:37 +0530 Subject: [PATCH 139/164] load text embeddings for memories from assets --- .../photos/assets/ml/text_embeddings.json | 3530 +++++++++++++++++ .../lib/services/smart_memories_service.dart | 28 +- .../lib/utils/text_embeddings_util.dart | 172 + mobile/apps/photos/pubspec.yaml | 1 + 4 files changed, 3714 insertions(+), 17 deletions(-) create mode 100644 mobile/apps/photos/assets/ml/text_embeddings.json create mode 100644 mobile/apps/photos/lib/utils/text_embeddings_util.dart diff --git a/mobile/apps/photos/assets/ml/text_embeddings.json b/mobile/apps/photos/assets/ml/text_embeddings.json new file mode 100644 index 0000000000..5714cbdc67 --- /dev/null +++ b/mobile/apps/photos/assets/ml/text_embeddings.json @@ -0,0 +1,3530 @@ +{ + "version": "1.0.0", + "embeddings": { + "clip_positive": { + "prompt": "Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion", + "vector": [ + -0.00029399772756733, -0.0024989808443933725, 0.0030610354151576757, + -0.04166121035814285, 0.03460526093840599, 0.050619494169950485, + -0.0188677366822958, 0.017285337671637535, -0.00911393016576767, + -0.013402838259935379, 0.0120625551789999, 0.0006917593418620527, + -0.02497251331806183, -0.022732943296432495, 0.029347892850637436, + 0.5122391581535339, -0.01061850693076849, -0.003770088544115424, + 0.012226847000420094, -0.05049843713641167, 0.0034501501359045506, + -0.02298370562493801, -0.03317851200699806, 0.0007177003426477313, + 0.03588501736521721, -0.05409558489918709, 0.038496412336826324, + 0.02857831120491028, -0.01419836189597845, 0.016654107719659805, + -0.010687682777643204, -0.0005015255883336067, 0.011595616117119789, + -0.020847897976636887, -0.011708027683198452, -0.020311785861849785, + -0.007099180947989225, -0.008629699237644672, -0.025595098733901978, + -0.028958778828382492, 0.014060010202229023, -0.020164787769317627, + 0.01764851063489914, -0.006684124935418367, -0.0515793077647686, + -0.034864671528339386, 0.006900300271809101, -0.015357058495283127, + -0.010073745623230934, 0.02983212284743786, 0.02481686696410179, + 0.05888601765036583, 0.008344347588717937, -0.04051980748772621, + -0.023208526894450188, -0.008880460634827614, -0.02613985724747181, + -0.017466925084590912, -0.02899336628615856, 0.007574765477329493, + 0.005715662147849798, 0.0017726332880556583, 0.03105134889483452, + 0.0461922325193882, -0.07244450598955154, 0.03219275176525116, + -0.013783305883407593, -0.008759403601288795, 0.041064564138650894, + 0.1308203488588333, 0.010998974554240704, 0.002741096541285515, + -0.006476596929132938, -0.007246179506182671, -0.003692265832796693, + 0.0019196323119103909, -0.05541857331991196, -0.024730399250984192, + -0.013100193813443184, -0.022196829319000244, -0.028197843581438065, + -0.02845725230872631, 0.05446740612387657, 0.025188688188791275, + 0.00250762770883739, 0.000709053419996053, 0.005534074734896421, + -0.01690487004816532, 0.036066606640815735, -0.02184230275452137, + 0.026598148047924042, -0.004738552030175924, 0.011768556199967861, + 0.0007177003426477313, -0.006554420106112957, 0.012996429577469826, + -0.012676490470767021, 0.008292465470731258, -0.013281780295073986, + -0.014008128084242344, 0.024574752897024155, 0.02172989211976528, + -0.026814322918653488, 0.0041851443238556385, 0.020839251577854156, + -0.004401318728923798, -0.06879547238349915, -0.01034180261194706, + 0.0008992872317321599, 0.010436920449137688, -0.014950649812817574, + -0.020406901836395264, 0.13213469088077545, -0.009822983294725418, + -0.014103244990110397, -0.01761392317712307, 0.018366212025284767, + 0.013108840212225914, -0.0133509561419487, 0.0009944040793925524, + 0.013437426649034023, -0.025984210893511772, -0.011457265354692936, + 0.008110878989100456, 0.02050201967358589, 0.03446691110730171, + 0.021470481529831886, 0.002836213679984212, -0.00470396364107728, + 0.003147505223751068, -0.04847504198551178, 0.002239570952951908, + 0.016213109716773033, -0.013394190929830074, -0.005931836552917957, + -0.033143918961286545, -0.02115054242312908, -0.0012451668735593557, + -0.002792978659272194, 0.007669882383197546, 0.019818907603621483, + -0.007133768871426582, 0.01491606142371893, -0.023805169388651848, + 0.005404370371252298, -0.0260793287307024, -0.019075265154242516, + 0.010350449942052364, -0.009710571728646755, 0.022041182965040207, + -0.031284816563129425, -0.012953193858265877, -0.025223277509212494, + -0.018997443839907646, -0.042716141790151596, -0.05844501778483391, + 0.03895470127463341, -0.01744963228702545, 0.024635281413793564, + 0.023364173248410225, -0.01995725743472576, 0.009010165929794312, + -0.003735500853508711, -0.006571713835000992, -0.039144936949014664, + -0.030774645507335663, 0.02484280988574028, -0.5175830125808716, + -0.00013835188292432576, -0.029460303485393524, -0.04680616781115532, + 0.07913727313280106, 0.001184638007543981, -0.0010376391001045704, + 0.0103677436709404, 0.013930304907262325, -0.03406050056219101, + -0.022041182965040207, -0.0009165811934508383, 0.038513705134391785, + 0.007324002683162689, -0.0007522883242927492, 0.0077649992890655994, + -0.0019109854474663734, -0.013212604448199272, -0.035461317747831345, + 0.014717180281877518, -0.016334168612957, 0.02303558774292469, + -0.028232431039214134, -0.019723789766430855, -0.039118994027376175, + 0.04071004316210747, -0.03521054983139038, 0.03808135166764259, + -0.033066097646951675, 0.022058477625250816, -0.056828033179044724, + -0.03027312271296978, -0.016766518354415894, 0.04006151854991913, + -0.06835447251796722, 0.03892011195421219, 0.0032945042476058006, + -0.014587475918233395, 0.010289921425282955, 0.05095672607421875, + -0.015063060447573662, -0.008396229706704617, -0.017302630469202995, + -0.02613985724747181, 0.023173939436674118, -0.058012671768665314, + 0.038505055010318756, -0.025067631155252457, -0.017795508727431297, + 0.03590231016278267, 0.004807727411389351, 0.022231418639421463, + -0.029304657131433487, -0.021980654448270798, -0.016558989882469177, + -0.005413017235696316, 0.0230528824031353, 0.025819920003414154, + 0.012693784199655056, -0.010921151377260685, 0.0011586970649659634, + 0.026165800169110298, -0.04114238917827606, -0.02717749774456024, + 0.017155632376670837, -0.010549331083893776, -0.019057970494031906, + -0.007254826370626688, -0.024756338447332382, -0.0039170878008008, + -0.022854000329971313, -0.007220238912850618, -0.00874210987240076, + 0.001642928458750248, 0.017216162756085396, -0.03951675444841385, + 0.006787888705730438, -0.08730003982782364, -0.004505082964897156, + -0.04747198894619942, 0.03839264437556267, -0.0714760422706604, + -0.02231788821518421, 0.02241300418972969, -0.01946437917649746, + -0.037977591156959534, 0.05524563416838646, -0.05752843618392944, + -0.0018331623869016767, -0.022577296942472458, -0.02236112207174301, + -0.00878534372895956, 0.0025595095939934254, 0.014976590871810913, + -0.03655948489904404, -0.005629192106425762, 0.002792978659272194, + 0.020303137600421906, -0.03268563002347946, 0.006104776635766029, + -0.03560831397771835, 0.014475065283477306, 0.03400862216949463, + -0.028379429131746292, 0.002991859335452318, -0.007384531665593386, + -0.02546539157629013, 0.028102725744247437, 0.02373599447309971, + 0.019879436120390892, -0.013644954189658165, -0.031820930540561676, + -0.05662915110588074, -0.008214643225073814, -0.03750200942158699, + -0.009260928258299828, 0.005101725459098816, 0.012287376448512077, + -0.0027583905030041933, -0.029373833909630775, 0.013316367752850056, + -0.0017380454810336232, -0.037839241325855255, -0.026969969272613525, + -0.017345866188406944, -0.03754524141550064, -0.026451149955391884, + -0.000432349625043571, 0.020432842895388603, -0.05502945929765701, + 0.12296022474765778, 0.03770088404417038, -0.00823193695396185, + -0.01582399755716324, -0.002083925064653158, -0.003778735874220729, + -0.021487776190042496, 0.033636800944805145, 0.012218199670314789, + 0.026831617578864098, 0.03803811967372894, -0.047506578266620636, + -0.006044247653335333, 0.08365964889526367, 0.02974565327167511, + 0.04988449811935425, -0.009900806471705437, 0.019628673791885376, + -0.041557442396879196, -0.051933836191892624, -0.03379244729876518, + -0.036023370921611786, -0.02920953929424286, 0.0035193259827792645, + 0.03687077388167381, -0.051933836191892624, 0.03700912743806839, + -0.046927228569984436, -0.0038652056828141212, 0.0414709746837616, + 0.012909960001707077, -0.032296519726514816, -0.037899766117334366, + 0.023312291130423546, 0.0060356007888913155, 0.02307017706334591, + 0.04563017934560776, -0.01060121227055788, -0.008672933094203472, + -0.028673425316810608, 0.06280311197042465, 0.016109347343444824, + -0.011050855740904808, 0.007799586746841669, 0.013878422789275646, + 0.026503032073378563, 0.015443528071045876, 0.034890614449977875, + 0.0038652056828141212, -0.01761392317712307, 0.0453534759581089, + -0.022611886262893677, -0.010203450918197632, -0.021435894072055817, + 0.03638654574751854, -0.01030721515417099, -0.008880460634827614, + -0.0024643929209560156, 0.01574617438018322, 0.048907388001680374, + -0.007324002683162689, -0.01999184675514698, -0.0032772100530564785, + 0.006148011423647404, -0.017821451649069786, 0.022888587787747383, + -0.00996998231858015, 0.00530060613527894, -0.0028535074088722467, + 0.012174965813755989, -0.007263473235070705, -0.0059664249420166016, + 0.027722256258130074, -0.03201116621494293, 0.012140377424657345, + -0.012339258566498756, -0.03099946863949299, 0.02184230275452137, + -0.023355526849627495, 0.04307067021727562, 0.019663261249661446, + -0.15298259258270264, 0.009044754318892956, 0.0033896209206432104, + 0.0019369263900443912, -0.026433855295181274, -0.002127160085365176, + 0.0015564586501568556, -0.010557977482676506, -0.0012711079325526953, + 0.00004323495886637829, 0.03380109369754791, -0.06299334019422531, + 0.040390100330114365, -0.05773596838116646, 0.023208526894450188, + 0.027644433081150055, 0.005490840412676334, 0.02726396732032299, + -0.006433362141251564, -0.026814322918653488, -0.015296529047191143, + -0.014207008294761181, 0.008915049023926258, -0.034328557550907135, + 0.0038392646238207817, -0.023441996425390244, -0.0007177003426477313, + 0.030688177794218063, -0.06485243886709213, -0.06524156033992767, + -0.010350449942052364, -0.0582980215549469, 0.02074413374066353, + -0.012166318483650684, -0.010497448965907097, -0.06057218462228775, + 0.01513223722577095, -0.008854520507156849, 0.03753659501671791, + -0.06895976513624191, 0.011431324295699596, 0.004055439494550228, + 0.05822884663939476, -0.0021876890677958727, -0.017743628472089767, + -0.008845873177051544, -0.010142921470105648, -0.0071164751425385475, + 0.03211492672562599, 0.008889107964932919, 0.01885044202208519, + 0.014673946425318718, 0.02731584943830967, 0.016636813059449196, + 0.011647499166429043, -0.008370288647711277, -0.06652131676673889, + 0.03981940075755119, -0.012806195765733719, 0.001418106839992106, + 0.01152644120156765, 0.001720751402899623, 0.008889107964932919, + -0.04371919482946396, -0.013307721354067326, 0.004686669912189245, + 0.0047644926235079765, -0.019144441932439804, -0.00824058335274458, + -0.00530060613527894, -0.00408138008788228, -0.04931379854679108, + 0.04058033600449562, 0.0005620545125566423, 0.02373599447309971, + 0.023874346166849136, 0.0047644926235079765, -0.005854013841599226, + -0.01065309438854456, 0.00815411377698183, -0.0022655120119452477, + -0.003303151112049818, 0.012347905896604061, -0.005646485835313797, + -0.005075784400105476, -0.0066149490885436535, 0.01877262070775032, + -0.05195113271474838, -0.000821464229375124, 0.030852466821670532, + 0.061609819531440735, -0.02860425040125847, -0.003311798209324479, + 0.0008560522692278028, 0.00019023384083993733, -0.03269427642226219, + -0.03091299906373024, -0.01030721515417099, 0.012598667293787003, + -0.024453694000840187, 0.017138339579105377, 0.016775164753198624, + -0.04474818706512451, 0.0442812480032444, 0.045647475868463516, + -0.02374464087188244, -0.07572171837091446, -0.0068311239592731, + -0.16558989882469177, 0.021409953013062477, -0.004202438518404961, + 0.03092164546251297, -0.01999184675514698, 0.046512171626091, + -0.00654577324166894, 0.029901299625635147, 0.007998468354344368, + -0.004193791188299656, -0.022542709484696388, -0.04104727506637573, + -0.010238038375973701, -0.01581534929573536, 0.014379948377609253, + -0.014959297142922878, -0.017890628427267075, 0.0078774094581604, + 0.01431941892951727, 0.002749743638560176, -0.034916553646326065, + 0.03393079712986946, 0.00498931435868144, 0.014258889481425285, + 0.007609352935105562, -0.03220139816403389 + ] + }, + "people_activities": { + "admiring": { + "prompt": "Photo of two people admiring or looking at each other in a loving but non-intimate and non-physical way", + "vector": [ + -0.03514610603451729, -0.02812313660979271, -0.03186924010515213, + 0.0643484964966774, 0.015219029039144516, 0.025284234434366226, + 0.007867596112191677, 0.0054588294588029385, -0.03783641383051872, + -0.061493948101997375, 0.027771208435297012, -0.01790934056043625, + -0.013334247283637524, -0.014499526470899582, 0.025425007566809654, + 0.5175095200538635, 0.023548046126961708, 0.02630092203617096, + -0.013521943241357803, -0.004700224380940199, -0.0021350437309592962, + -0.00952557846903801, -0.008117858320474625, 0.000656936492305249, + -0.0020646576303988695, -0.011770112439990044, -0.0049817683175206184, + -0.021170560270547867, -0.021561594679951668, 0.035865604877471924, + -0.001986450981348753, -0.00030500622233375907, 0.03502097353339195, + -0.04644697532057762, -0.026433873921632767, -0.00983840599656105, + -0.04971601441502571, 0.03028164431452751, -0.013514121994376183, + -0.043123189359903336, 0.010245081037282944, 0.0002502615097910166, + -0.012333200313150883, 0.0380319319665432, -0.011332154273986816, + -0.026715418323874474, -0.016024557873606682, -0.053931355476379395, + -0.011457285843789577, 0.03927541896700859, -0.0016579825896769762, + 0.03470032289624214, 0.005028692539781332, 0.02120966464281082, + 0.018581919372081757, 0.008375939913094044, 0.03340991586446762, + 0.0073123290203511715, -0.033738382160663605, -0.01745574176311493, + -0.023508941754698753, 0.026191432029008865, -0.01151202991604805, + 0.06702316552400589, -0.015672627836465836, 0.0440773107111454, + 0.0014468245208263397, -0.0020020920783281326, 0.011918704956769943, + 0.08666869252920151, -0.033965181559324265, 0.03676498308777809, + 0.02721594087779522, 0.040456339716911316, -0.004653300624340773, + -0.01441350020468235, -0.09551387280225754, 0.03524777293205261, + 0.02456473372876644, -0.0435924269258976, -0.03026600182056427, + -0.03004702553153038, 0.019105903804302216, -0.01288846880197525, + 0.005091257859021425, 0.0017909340094774961, -0.0007586052524857223, + -0.021913524717092514, -0.004708044696599245, 0.03254964202642441, + 0.013279502280056477, 0.02537808194756508, -0.01927795819938183, + 0.0407300628721714, -0.033065807074308395, 0.05871760845184326, + 0.0424584299325943, -0.012638206593692303, 0.0023696639109402895, + 0.020411955192685127, 0.0015719551593065262, -0.02852199412882328, + 0.0037695644423365593, -0.01983322575688362, 0.0449688695371151, + 0.06520094722509384, -0.06771138310432434, 0.036256637424230576, + -0.001133997575379908, -0.007437459193170071, 0.01277115847915411, + -0.008985953405499458, 0.15607717633247375, 0.03256528079509735, + 0.018472427502274513, -0.037468839436769485, 0.005607421975582838, + -0.000656936492305249, -0.010049563832581043, 0.030602293089032173, + 0.007640797644853592, -0.028170062229037285, 0.0023774844594299793, + 0.0002033374912571162, 0.010229440405964851, -0.0038164884317666292, + -0.000383212958695367, -0.018433324992656708, 0.001313872984610498, + -0.04119930416345596, 0.015125180594623089, 0.005896787159144878, + -0.0011496389051899314, 0.0011418182402849197, 0.05330570414662361, + 0.0010870734695345163, 0.0002502615097910166, 0.01318565383553505, + 0.038243088871240616, -0.004215342458337545, 0.00880607683211565, + -0.0019395267590880394, -0.024056388065218925, 0.0031908343080431223, + -0.002705952851101756, -0.03058665059506893, -0.022445330396294594, + 0.03086819313466549, -0.05810759961605072, -0.033957358449697495, + -0.0183003731071949, 0.028185702860355377, -0.04283382371068001, + 0.03940054774284363, 0.03269823268055916, 0.013287322595715523, + 0.02150684967637062, 0.01159023679792881, 0.01299795787781477, + -0.030156515538692474, -0.005013051442801952, 0.011339975520968437, + -0.058287475258111954, 0.024901021271944046, -0.04110545292496681, + 0.031525131314992905, 0.021233126521110535, -0.5212634801864624, + 0.004629838280379772, -0.018581919372081757, -0.012685131281614304, + 0.08338400721549988, -0.0017987547907978296, 0.018628841266036034, + -0.013091806322336197, 0.0011261767940595746, 0.022773798555135727, + -0.034129414707422256, 0.022398406639695168, -0.01181703619658947, + -0.0018143960041925311, 0.014280547387897968, -0.02295367419719696, + -0.027935443446040154, 0.010315467603504658, -0.020005280151963234, + -0.03294849395751953, -0.010722142644226551, 0.01478889212012291, + -0.01216114591807127, -0.025135641917586327, -0.0015250311698764563, + 0.045164383947849274, -0.02295367419719696, -0.01304488256573677, + -0.039541322737932205, 0.013576687313616276, 0.05171810835599899, + -0.03906426206231117, 0.01678316295146942, -0.017354073002934456, + 0.02462729811668396, -0.003417633706703782, 0.03437967598438263, + 0.010440598241984844, -0.026777982711791992, 0.001618879148736596, + -0.031994372606277466, -0.01502351276576519, 0.012434869073331356, + 0.01632174476981163, -0.03318311646580696, -0.055472031235694885, + -0.002095940290018916, -0.0010557908099144697, -0.0758214220404625, + -0.009236213751137257, -0.03192398324608803, -0.007320149801671505, + -0.0029874970205128193, 0.0059046074748039246, -0.024955766275525093, + -0.013709639199078083, 0.051647722721099854, -0.01044841855764389, + -0.03086819313466549, 0.021929167211055756, -0.024134594947099686, + -0.01303706131875515, -0.010683038271963596, -0.03432493284344673, + -0.02806839346885681, 0.007140273693948984, -0.04942664876580238, + -0.019567323848605156, 0.035200849175453186, -0.01064393576234579, + -0.04413987696170807, -0.03332388401031494, -0.02324303798377514, + 0.01822216622531414, 0.0031830137595534325, -0.03575611487030983, + 0.021225305274128914, 0.004246625117957592, 0.006279999855905771, + -0.01353758480399847, -0.007335790432989597, -0.07419472187757492, + -0.04053454473614693, 0.006381668616086245, -0.04254445806145668, + 0.027333252131938934, 0.01059701107442379, 0.02210904285311699, + -0.012520897202193737, -0.005599601659923792, 0.054330214858055115, + -0.02067003771662712, 0.037703461945056915, -0.027638258412480354, + -0.013787846080958843, 0.03139999881386757, 0.061705105006694794, + -0.02394689992070198, -0.0218900628387928, 0.01943437196314335, + 0.027192478999495506, 0.009595965966582298, -0.005404084920883179, + 0.0025182566605508327, -0.014585554599761963, -0.0285845585167408, + -0.031665902584791183, 0.04905908182263374, 0.014202342368662357, + -0.01342027448117733, -0.006068842019885778, -0.0059202490374445915, + -0.033214397728443146, 0.05482291802763939, 0.04771392419934273, + -0.026011556386947632, 0.016220074146986008, 0.05205439776182175, + 0.04306844249367714, -0.02675452083349228, 0.016525082290172577, + -0.04948921501636505, -0.035357262939214706, 0.006913474760949612, + -0.006960398517549038, -0.05734899267554283, -0.018941668793559074, + 0.01866794563829899, 0.004614196717739105, -0.03132961690425873, + 0.015195567160844803, 0.048081494867801666, 0.021240947768092155, + 0.0073279705829918385, 0.002627745969220996, 0.013553226366639137, + 0.026410412043333054, 0.003980722278356552, -0.028248269110918045, + -0.031611159443855286, 0.007492204196751118, -0.0015406724996864796, + 0.03597509488463402, 0.032831184566020966, -0.0025730011984705925, + -0.009658530354499817, -0.03331606462597847, -0.015563137829303741, + 0.004801893141120672, -0.06827446818351746, 0.02409549243748188, + -0.05810759961605072, -0.0327138751745224, 0.04088647663593292, + 0.05888184532523155, 0.014085031114518642, 0.04640787094831467, + -0.0346846841275692, -0.013561046682298183, -0.019864508882164955, + 0.04460911825299263, -0.019262315705418587, -0.0020411955192685127, + 0.020028743892908096, -0.02737235277891159, 0.04531297832727432, + 0.044327571988105774, -0.0480189323425293, -0.005888966377824545, + 0.0035349440295249224, -0.021006327122449875, -0.030696140602231026, + -0.02301623858511448, -0.01360797043889761, -0.013013599440455437, + 0.03806321322917938, 0.010870734229683876, -0.014311830513179302, + -0.016634570434689522, 0.0050208717584609985, -0.005302416160702705, + -0.04763571545481682, -0.0004535990010481328, -0.027028243988752365, + 0.038462068885564804, -0.00024244084488600492, -0.05312582850456238, + 0.016110586002469063, 0.04937972500920296, -0.05849863216280937, + -0.03499750792980194, 0.010815990157425404, 0.029616886749863625, + -0.018636662513017654, -0.019629888236522675, -0.001094894134439528, + 0.01528941374272108, 0.03591252863407135, -0.03255745768547058, + -0.00044577830703929067, -0.037007421255111694, 0.03003138303756714, + 0.006577185820788145, -0.027880696579813957, -0.04398346319794655, + -0.03299541771411896, -0.010033923201262951, 0.04370191693305969, + -0.01159023679792881, 0.03227591514587402, -0.04453872889280319, + -0.07761235535144806, 0.0002971855574287474, -0.049590885639190674, + -0.015680449083447456, -0.029718557372689247, 0.008438506163656712, + 0.02980458363890648, 0.008485429920256138, -0.005411905702203512, + -0.007202839478850365, 0.026402590796351433, -0.04015133157372475, + -0.07478127628564835, 0.007476563099771738, 0.01861320249736309, + -0.011379078961908817, -0.02295367419719696, -0.0015406724996864796, + -0.001290410989895463, -0.031063711270689964, -0.03436403349041939, + -0.11970321834087372, -0.02407984994351864, -0.0426461286842823, + -0.023657534271478653, -0.014968767762184143, -0.030633574351668358, + -0.018511531874537468, 0.020232081413269043, 0.011371258646249771, + 0.006788343656808138, -0.02462729811668396, -0.024431781843304634, + 0.01577429659664631, 0.01730714924633503, 0.011050610803067684, + 0.04086301475763321, 0.05634012445807457, 0.011191382072865963, + -0.028170062229037285, 0.007828493602573872, 0.027489662170410156, + 0.045492853969335556, -0.03988543152809143, -0.01239576656371355, + 0.0060923038981854916, -0.022586101666092873, -0.007562590297311544, + 0.006506799720227718, -0.023892154917120934, 0.04185623675584793, + -0.025659628212451935, -0.02623053640127182, -0.021780572831630707, + 0.0010323288151994348, 0.0032221172004938126, -0.05886620655655861, + 0.024204982444643974, 0.032526176422834396, -0.002189788268879056, + -0.013733101077377796, -0.01262256596237421, -0.002056836849078536, + -0.0275991540402174, 0.024197161197662354, 0.01943437196314335, + -0.0056856293231248856, 0.03224463388323784, -0.008915566839277744, + 0.030477160587906837, -0.0035896888002753258, -0.05702834203839302, + -0.03987761214375496, -0.015602242201566696, 0.010385853238403797, + -0.021084534004330635, 0.006827447097748518, -0.001994271529838443, + 0.02142864279448986, 0.03279989957809448, 0.014929663389921188, + 0.037703461945056915, -0.0015719551593065262, 0.008712229318916798, + -0.027739927172660828, -0.03369927778840065, 0.007468742318451405, + 0.04313100874423981, -0.03158769756555557, 0.0424036867916584, + -0.0005318057374097407, 0.02143646404147148, -0.016603287309408188, + 0.013443736359477043, -0.03951003775000572, 0.02334470860660076, + -0.0018769614398479462, -0.010995865799486637, 0.0017127272440120578, + -0.005333698354661465, -0.008939028717577457, 0.018355118110775948, + 0.018574098125100136, 0.10098834335803986, 0.0284359659999609, + -0.016517261043190956, 0.023070983588695526, -0.026715418323874474, + -0.10452328622341156, -0.018808718770742416, 0.006491158157587051, + 0.002627745969220996, 0.025417186319828033, 0.03117320127785206, + 0.035106997936964035, 0.003480199258774519, 0.006592826917767525, + 0.01292757224291563, 0.021029789000749588, -0.029085082933306694, + 0.009580324403941631, -0.00019551682635210454, -0.021905705332756042, + 0.004966127220541239, 0.0443197526037693, 0.007953624241054058, + 0.0203650314360857, 0.02370445802807808, -0.000766425917390734, + 0.01683790795505047, 0.010628294199705124, 0.002705952851101756, + 0.005896787159144878, -0.011989091522991657 + ] + }, + "embracing": { + "prompt": "Photo of people hugging or embracing each other lovingly, without inappropriately kissing or other intimate actions", + "vector": [ + 0.03190479800105095, 0.012113233096897602, -0.022465744987130165, + 0.020599115639925003, 0.026688827201724052, -0.00448123412206769, + 0.016892332583665848, 0.035790298134088516, -0.07396353781223297, + -0.04633476957678795, 0.010511374101042747, -0.010220126248896122, + 0.014443210326135159, 0.01785212568938732, 0.06136709451675415, + 0.5228084325790405, -0.00482543557882309, -0.001628336263820529, + -0.03713400661945343, -0.01580677554011345, -0.032546866685152054, + -0.02028800919651985, -0.060705170035362244, 0.004487853497266769, + 0.010140695609152317, 0.011874939315021038, -0.04372016713023186, + 0.00482543557882309, -0.009644251316785812, 0.041899871081113815, + -0.039265409111976624, -0.016250265762209892, 0.0030117600690573454, + -0.05602535605430603, -0.022598128765821457, -0.0026146050076931715, + -0.0355321504175663, 0.041357092559337616, 0.016131119802594185, + -0.024047747254371643, 0.029846210032701492, -0.009472151286900043, + -0.01952679641544819, 0.04841983690857887, -0.003971552010625601, + -0.05485374853014946, -0.02879374846816063, -0.05014083907008171, + -0.014535878784954548, 0.012867826968431473, -0.028178159147500992, + 0.08048349618911743, -0.0032235761173069477, 0.0004103936953470111, + -0.029998455196619034, -0.017263010144233704, 0.03011760115623474, + 0.020751357078552246, 0.0015224282396957278, -0.0069435965269804, + -0.026126191020011902, 0.006460390519350767, 0.006824449636042118, + 0.015098516829311848, -0.03204380348324776, 0.024511093273758888, + -0.005977185443043709, 0.01679966412484646, 0.00009266954293707386, + 0.04995550215244293, -0.017514541745185852, 0.030223509296774864, + 0.04506387189030647, 0.009313289076089859, 0.002720512915402651, + -0.026251958683133125, -0.0866791158914566, 0.035022467374801636, + 0.02159862220287323, -0.015065419487655163, -0.047625523060560226, + -0.024173511192202568, -0.004501091782003641, -0.027092603966593742, + -0.0005030632601119578, -0.0006751638138666749, -0.013264983892440796, + -0.0020718262530863285, 0.024703051894903183, 0.000052954022976337, + -0.02320048026740551, 0.00013900430349167436, -0.021863391622900963, + 0.04317738488316536, 0.03924554958939552, 0.05359609052538872, + 0.025940852239727974, -0.01834856905043125, 0.00448123412206769, + 0.006056616548448801, 0.006797972600907087, -0.01000831089913845, + -0.024696432054042816, -0.033784665167331696, 0.033579468727111816, + 0.000105908045952674, -0.061559051275253296, 0.034195058047771454, + -0.0034552502911537886, -0.005672699771821499, -0.020228436216711998, + -0.014595451764762402, 0.1076025739312172, -0.028734175488352776, + -0.012219141237437725, -0.04081431403756142, 0.015131612308323383, + -0.016806283965706825, 0.008889656513929367, 0.025762131437659264, + 0.03790184110403061, -0.006811210885643959, -0.04401141032576561, + -0.013119358569383621, 0.023597635328769684, 0.011120345443487167, + 0.03059418871998787, -0.02412055805325508, 0.02811196632683277, + 0.014039434492588043, -0.016395889222621918, -0.008532216772437096, + 0.0004699669370893389, 0.013364271260797977, 0.09090881794691086, + -0.043428920209407806, -0.03463193029165268, 0.022988665848970413, + 0.03298373892903328, -0.020506445318460464, -0.015171327628195286, + -0.031130345538258553, -0.0036803046241402626, -0.043462011963129044, + -0.04213154315948486, -0.017481446266174316, -0.031805507838726044, + 0.006612633820623159, -0.0343870185315609, -0.0011252729455009103, + -0.04397169500589371, -0.00045010916073806584, -0.05562158301472664, + 0.02931005321443081, 0.002177734160795808, -0.026728542521595955, + 0.007737906649708748, -0.008671221323311329, 0.0023564540315419436, + -0.0002581508597359061, -0.013212029822170734, -0.03402295708656311, + -0.05244433879852295, 0.017329204827547073, -0.023498348891735077, + 0.015482432208955288, 0.031825367361307144, -0.5259922742843628, + 0.005176255479454994, 0.014251251704990864, -0.0660402849316597, + 0.07855729013681412, 0.0002912471245508641, 0.004295895341783762, + -0.016124499961733818, 0.005341737065464258, 0.02462361939251423, + 0.01058418583124876, 0.0008538836264051497, -0.015608198940753937, + 0.0002382930979365483, 0.010994579643011093, -0.005679319147020578, + -0.020420394837856293, 0.018613338470458984, -0.001542285899631679, + -0.01722329668700695, -0.027430184185504913, 0.008280685171484947, + -0.017124006524682045, 0.02515977993607521, -0.025808466598391533, + -0.020374059677124023, -0.03683614358305931, -0.03654489666223526, + 0.0013503276277333498, -0.005626365076750517, 0.03961622714996338, + -0.005308640655130148, 0.03429434821009636, 0.003104429692029953, + 0.009088234044611454, -0.002799944020807743, 0.018785439431667328, + 0.009756778366863728, 0.018434619531035423, -0.005910992622375488, + 0.012801635079085827, -0.03344046697020531, -0.020466729998588562, + 0.011537358164787292, -0.03674347326159477, -0.006725160870701075, + 0.016912192106246948, -0.02935638464987278, -0.007545948028564453, + 0.04967087507247925, -0.0028528980910778046, -0.014959512278437614, + 0.007115696556866169, -0.038292378187179565, -0.01070995070040226, + -0.0024623621720820665, 0.028264211490750313, -0.028806986287236214, + 0.00784381479024887, 0.017263010144233704, 0.011821985244750977, + 0.005771988537162542, 0.006149285938590765, -0.05329160392284393, + -0.02453095093369484, 0.05043208971619606, -0.08943934738636017, + -0.01077614352107048, 0.004527568817138672, 0.007757764775305986, + -0.017600594088435173, -0.028284067288041115, -0.015965638682246208, + 0.020771216601133347, -0.0007943103555589914, -0.051775798201560974, + 0.0035677773412317038, 0.03422153741121292, -0.0027469899505376816, + -0.026927120983600616, 0.02281656488776207, -0.050988104194402695, + -0.07974876463413239, -0.005169636569917202, -0.05307317152619362, + -0.005043870769441128, -0.03931836411356926, -0.024358851835131645, + 0.00539469113573432, -0.03316907584667206, 0.05090205371379852, + -0.013079644180834293, 0.015469194389879704, -0.037478212267160416, + -0.03344046697020531, 0.08413070440292358, 0.0449976809322834, + -0.0311104878783226, -0.038385048508644104, 0.023233577609062195, + 0.053238652646541595, -0.023835929110646248, -0.02187001146376133, + -0.023114431649446487, 0.008863179944455624, -0.01641574688255787, + -0.05323203280568123, 0.012192663736641407, 0.03041546605527401, + -0.0014496163930743933, -0.008221112191677094, -0.010279699228703976, + -0.007188509218394756, 0.047671858221292496, 0.03461207449436188, + -0.03889473155140877, -0.0011252729455009103, 0.04734089598059654, + 0.02732427604496479, 0.008935990743339062, 0.02318062260746956, + -0.02584156207740307, -0.04824111610651016, 0.012278714217245579, + -0.025199495255947113, -0.05007464811205864, -0.002343215513974428, + 0.010273081250488758, 0.012656011618673801, 0.002171115018427372, + 0.030355893075466156, 0.030475042760372162, -0.020499825477600098, + -0.004666573368012905, 0.006215478293597698, 0.0042429412715137005, + -0.00016548130952287465, 0.015992116183042526, -0.008234350942075253, + 0.018653053790330887, -0.014079151675105095, 0.01740863546729088, + 0.008088726550340652, 0.04458728805184364, 0.006791353691369295, + 0.0020916839130222797, 0.017554258927702904, -0.0005229209782555699, + 0.05415872484445572, -0.06349187344312668, -0.0031838605646044016, + -0.02958144061267376, 0.006738399155437946, 0.04008619859814644, + 0.015866348519921303, -0.00421646423637867, 0.047850579023361206, + -0.02380945347249508, -0.014628549106419086, 0.007744526024907827, + 0.006592776160687208, -0.022055350244045258, 0.025351738557219505, + -0.04393859952688217, 0.005540314596146345, 0.03927202895283699, + 0.06421337276697159, -0.02761552296578884, -0.012788397260010242, + 0.032990358769893646, -0.04202563688158989, 0.0013768046628683805, + 0.0024425042793154716, 0.005593268666416407, -0.01525075826793909, + 0.033513277769088745, -0.0014099008403718472, 0.04309795796871185, + 0.026748400181531906, -0.02103598602116108, 0.007850433699786663, + -0.05066376179456711, 0.039821427315473557, 0.027675095945596695, + 0.01834856905043125, 0.0060963318683207035, -0.03812689706683159, + 0.013814380392432213, 0.04603028669953346, -0.03898078203201294, + -0.02425956167280674, 0.011239491403102875, 0.011782269924879074, + 0.004527568817138672, -0.010756285861134529, -0.018163230270147324, + 0.013642280362546444, 0.03339413180947304, 0.008379973471164703, + 0.020844027400016785, -0.010570947080850601, -0.0039847902953624725, + 0.029945500195026398, -0.0016680517001077533, 0.027608904987573624, + -0.01834856905043125, -0.0027469899505376816, -0.012278714217245579, + 0.005076967179775238, 0.022373074665665627, -0.027423566207289696, + -0.07132244855165482, 0.013827620074152946, -0.025292163714766502, + -0.011716078035533428, -0.051193300634622574, -0.0197253730148077, + 0.02865474671125412, -0.019718755036592484, -0.05842152610421181, + -0.01763368956744671, 0.014390256255865097, -0.027966342866420746, + -0.09944765269756317, -0.054483070969581604, 0.01760721392929554, + 0.004534188192337751, -0.03393691033124924, -0.005275544710457325, + 0.006420675665140152, -0.03853728994727135, -0.03293078392744064, + -0.1083240807056427, 0.01916273683309555, -0.04758581146597862, + -0.007353989407420158, -0.04179396107792854, -0.009816352277994156, + -0.00542778754606843, 0.008379973471164703, -0.0049776784144341946, + 0.02719189040362835, -0.03471798077225685, -0.04080107435584068, + -0.013681996613740921, 0.027469897642731667, -0.004606999922543764, + 0.04767847806215286, 0.01767340488731861, -0.009968595579266548, + -0.011318922974169254, -0.018427999690175056, 0.0280259158462286, + 0.11136892437934875, -0.005540314596146345, -0.0007479756022803485, + 0.015184566378593445, 0.022465744987130165, 0.032725587487220764, + 0.022399552166461945, -0.06292261928319931, -0.010637139901518822, + -0.029885927215218544, -0.004501091782003641, -0.008022534660995007, + 0.017501303926110268, -0.02910485491156578, -0.0008274066494777799, + 0.04789029434323311, 0.005639603361487389, 0.0008207873906940222, + 0.03124949149787426, 0.017885221168398857, -0.03352651372551918, + 0.03794817626476288, 0.0012973735574632883, 0.0005692557897418737, + -0.001979156630113721, 0.02885994128882885, -0.02382931113243103, + 0.014529259875416756, -0.04982311651110649, -0.07375171780586243, + -0.026046760380268097, -0.004606999922543764, 0.020665308460593224, + -0.020870503038167953, 0.0, -0.013940147124230862, + 0.020387299358844757, 0.024828817695379257, -0.0035082038957625628, + -0.009074995294213295, -0.00007281178113771603, + -0.0051630171947181225, -0.016309838742017746, -0.005851419642567635, + -0.012404480017721653, 0.0197452325373888, -0.015820013359189034, + 0.009505246765911579, -0.0018335330532863736, 0.012920781038701534, + 0.006208859384059906, 0.006586156319826841, 0.014151962473988533, + -0.0014099008403718472, -0.02750299498438835, -0.015052181668579578, + 0.001542285899631679, 0.015045562759041786, 0.011610169894993305, + 0.025424549356102943, -0.0015224282396957278, 0.08096670359373093, + 0.020519685000181198, 0.014403493143618107, -0.01109386794269085, + -0.05837519094347954, -0.08014591038227081, 0.011603550054132938, + -0.003686923999339342, 0.009551581926643848, 0.021049223840236664, + -0.012841351330280304, 0.013463560491800308, -0.02214140072464943, + 0.016601087525486946, 0.018249280750751495, -0.009617773815989494, + -0.003931836225092411, 0.017931556329131126, 0.007042884826660156, + -0.049743685871362686, 0.006930357776582241, 0.006222097668796778, + 0.009114711545407772, 0.01834856905043125, -0.012656011618673801, + -0.0026410820428282022, 0.05188170447945595, 0.04044363647699356, + 0.02556355483829975, 0.036624327301979065, -0.049227382987737656 + ] + }, + "party": { + "prompt": "Photo of people celebrating together", + "vector": [ + 0.021657669916749, 0.02267414890229702, -0.03314313665032387, + -0.04715124890208244, -0.000511949067004025, -0.0014542320277541876, + -0.0359848253428936, 0.02541196346282959, -0.016916576772928238, + -0.03479769825935364, 0.015351051464676857, -0.030872752889990807, + -0.013614876195788383, 0.027786219492554665, 0.01966923102736473, + 0.5486461520195007, 0.01717626117169857, 0.006358555518090725, + 0.004251402802765369, -0.031265988945961, 0.010513504035770893, + -0.023905795067548752, -0.013889400288462639, 0.008072471246123314, + -0.00276007317006588, 0.0044517312198877335, -0.019639553502202034, + 0.019068246707320213, 0.0030494355596601963, 0.0061804866418242455, + 0.011262878775596619, -0.019105345010757446, 0.011819345876574516, + -0.030338546261191368, 0.026970067992806435, 0.004889484494924545, + -0.01432715356349945, -0.0034723500721156597, 0.032045044004917145, + 0.021961871534585953, 0.050638437271118164, -0.015625575557351112, + 0.0026710384991019964, 0.03608127683401108, -0.01342196948826313, + -0.04062946140766144, -0.04177207499742508, -0.021271854639053345, + 0.007694074884057045, 0.020099565386772156, -0.00014839103096164763, + 0.03710517659783363, 0.03411509469151497, 0.03371443971991539, + -0.005497887264937162, -0.0050081973895430565, -0.003316539339721203, + 0.003598482348024845, -0.00483012804761529, 0.0036429997999221087, + -0.03180761635303497, -0.01874178647994995, -0.010439308360219002, + -0.07591685652732849, -0.05038617178797722, 0.015766547992825508, + 0.008658615872263908, 0.018140802159905434, 0.0036504194140434265, + 0.03667484223842621, 0.02351255901157856, 0.023327069357037544, + -0.020492801442742348, 0.0028416882269084454, 0.03282409533858299, + 0.005416272673755884, -0.05294591560959816, 0.03551739454269409, + 0.038648445159196854, 0.0064624291844666, -0.015907518565654755, + -0.015202660113573074, -0.02860979177057743, 0.03502028435468674, + -0.027934610843658447, -0.0004525926196947694, 0.014750069007277489, + -0.012813565321266651, 0.0009497025748714805, 0.0012910020304843783, + -0.0749448910355568, -0.005668537225574255, -0.01867501251399517, + 0.0038433277513831854, 0.035465456545352936, 0.039842989295721054, + -0.06788889318704605, 0.024388065561652184, -0.021627992391586304, + 0.0019661812111735344, -0.014987492933869362, 0.017035290598869324, + 0.0014097146922722459, -0.03465672582387924, 0.016983352601528168, + 0.01953567937016487, -0.05583954229950905, 0.015009752474725246, + 0.008176345378160477, -0.002255543600767851, 0.006900182459503412, + -0.03729808330535889, 0.06202002987265587, -0.0035836431197822094, + -0.0038878447376191616, -0.07182125747203827, 0.021071525290608406, + -0.02357933484017849, 0.003858166979625821, 0.013926498591899872, + 0.04360470175743103, -0.02247382141649723, 0.004414633382111788, + -0.011218362487852573, 0.008220863528549671, -0.0069966367445886135, + -0.0021590893156826496, -0.05723441764712334, 0.025790361687541008, + -0.0016916577005758882, -0.018734367564320564, 0.03210439905524254, + -0.005178846884518862, 0.009422830305993557, 0.009838324971497059, + 0.003464930457994342, -0.009445088915526867, -0.009044433012604713, + 0.013681653887033463, 0.006299199070781469, -0.02753395587205887, + 0.004214304964989424, 0.008703134022653103, -0.04569701850414276, + -0.00042291442514397204, 0.026851356029510498, -0.030234670266509056, + 0.0006529205129481852, -0.010632217861711979, -0.004333017859607935, + -0.02006988599896431, -0.02068571001291275, -0.07169512659311295, + 0.027934610843658447, -0.020982490852475166, -0.010098009370267391, + -0.009007335640490055, 0.004844967275857925, 0.022303171455860138, + -0.026153918355703354, -0.020166339352726936, -0.023304810747504234, + -0.013763267546892166, 0.0, 0.011366752907633781, + -0.023230616003274918, 0.02135346829891205, -0.5525710582733154, + 0.010120267979800701, -0.026547156274318695, -0.007575361989438534, + 0.054407574236392975, -0.012294196523725986, 0.014431027695536613, + 0.006425331346690655, 0.011099648661911488, 0.026302309706807137, + 0.0069966367445886135, -0.019884398207068443, -0.008027954958379269, + 0.000022258655008045025, 0.007961179129779339, 0.03180019557476044, + 0.010216722264885902, 0.03761712461709976, -0.009504444897174835, + -0.010847384110093117, -0.00271555595099926, 0.03042016178369522, + -0.009556381963193417, 0.03968718275427818, 0.013948756270110607, + -0.004592702724039555, -0.01942438632249832, 0.004889484494924545, + -0.027563635259866714, -0.0013280997518450022, 0.0203518308699131, + -0.014831682667136192, 0.054207246750593185, -0.010290917940437794, + 0.029329488053917885, -0.01600397191941738, -0.02216220088303089, + 0.016946256160736084, 0.029908213764429092, -0.033261850476264954, + -0.008799588307738304, 0.015195241197943687, -0.00010387371730757877, + -0.00605435436591506, 0.003924942575395107, 0.04178691282868385, + -0.049599699676036835, -0.02055215835571289, -0.014193601906299591, + 0.018860500305891037, -0.03188923001289368, -0.037253569811582565, + -0.014816843904554844, -0.02642844244837761, 0.04197240248322487, + 0.0062546818517148495, 0.0545337051153183, 0.0038433277513831854, + 0.02878785878419876, 0.005304979160428047, 0.013607458211481571, + 0.0035094479098916054, 0.0005490467883646488, -0.0031162116210907698, + -0.010980935767292976, 0.006796309258788824, -0.02159089408814907, + -0.016642054542899132, -0.02638392336666584, -0.022110262885689735, + 0.0011055131908506155, -0.025864554569125175, -0.0061433888040483, + -0.024224834516644478, -0.009519284591078758, -0.02364611066877842, + 0.014935556799173355, -0.027571052312850952, 0.046246062964200974, + -0.010357693769037724, 0.02444000169634819, -0.03789164870977402, + -0.00968993455171585, -0.0007122769602574408, -0.018793722614645958, + -0.038336820900440216, 0.009170565754175186, 0.010847384110093117, + -0.009749290533363819, -0.028275910764932632, 0.006506946869194508, + -0.01429747510701418, 0.021538957953453064, -0.027867835015058517, + -0.026242952793836594, 0.018133383244276047, 0.04194272309541702, + 0.02547873929142952, -0.030895013362169266, 0.021895095705986023, + 0.0123832318931818, 0.006685015745460987, -0.01955793797969818, + -0.016211720183491707, 0.003086533397436142, -0.018289193511009216, + -0.03897490352392197, 0.03490899130702019, -0.019832462072372437, + 0.01676076650619507, -0.010060911998152733, 0.0003264602564740926, + 0.006640498526394367, -0.004563024267554283, 0.019565356895327568, + 0.025975851342082024, 0.0015803645364940166, 0.001528427586890757, + 0.0447324737906456, -0.025582613423466682, 0.0214350838214159, + -0.02679941989481449, 0.018519200384616852, -0.03084307350218296, + -0.03813649341464043, -0.03435994312167168, 0.0012539041927084327, + 0.025107761844992638, -0.0014022953109815717, -0.0023223196621984243, + 0.0049191624857485294, 0.025352606549859047, -0.011938057839870453, + 0.003976879641413689, 0.0017584336455911398, 0.025419384241104126, + -0.018400488421320915, 0.02435096725821495, 0.00271555595099926, + 0.01448296383023262, 0.014445867389440536, 0.016901738941669464, + 0.005312399007380009, -0.013043572194874287, 0.017391428351402283, + -0.00848796684294939, 0.011952897533774376, -0.020960234105587006, + -0.0005045294528827071, -0.05082392692565918, 0.004770771600306034, + -0.025419384241104126, 0.007456648629158735, 0.04484377056360245, + 0.0020997331012040377, -0.034582529217004776, 0.010550602339208126, + -0.011099648661911488, 0.01111448835581541, -0.008339575491845608, + 0.048338379710912704, -0.0051046512089669704, 0.009927359409630299, + 0.02717781625688076, -0.005557244177907705, 0.029997248202562332, + 0.0941837877035141, -0.0033536371774971485, 0.0005416272324509919, + 0.019899237900972366, -0.034078001976013184, 0.01092899963259697, + 0.006751792039722204, 0.0010387372458353639, -0.018712108954787254, + 0.09233631193637848, 0.0031458898447453976, -0.012665174901485443, + -0.0008606679039075971, -0.0024336129426956177, -0.011789667420089245, + -0.1166427731513977, -0.007790528703480959, -0.0031681484542787075, + 0.0247664637863636, -0.011307395994663239, -0.05511242896318436, + 0.012820985168218613, 0.01980278268456459, -0.12772756814956665, + 0.014490384608507156, 0.008228282444179058, -0.06526979058980942, + 0.04523700475692749, -0.007894402369856834, -0.04649832844734192, + -0.016048489138484, 0.04359728470444679, 0.027281688526272774, + 0.02774912118911743, 0.0015581058105453849, 0.008154086768627167, + 0.025048404932022095, 0.02400224842131138, 0.034582529217004776, + -0.008027954958379269, 0.008495386689901352, -0.010936419479548931, + 0.017866279929876328, 0.017799504101276398, -0.0154104083776474, + -0.09984490275382996, 0.0003042016178369522, -0.037698738276958466, + -0.005534985102713108, -0.04093366488814354, -0.014260378666222095, + 0.036036763340234756, -0.02932206727564335, -0.031473737210035324, + -0.025842297822237015, 0.033588312566280365, 0.018712108954787254, + -0.08542871475219727, -0.04044397547841072, 0.014156504534184933, + -0.00941541139036417, 0.007130189333111048, 0.007708914112299681, + 0.00834699533879757, -0.077052041888237, 0.020700549706816673, + -0.11950672417879105, 0.043901484459638596, -0.002745233941823244, + -0.05080166831612587, -0.040369778871536255, 0.003962040413171053, + 0.02371288649737835, 0.016901738941669464, 0.030368225648999214, + 0.0167978648096323, -0.04108205810189247, 0.006907602306455374, + 0.006284359842538834, -0.010943838395178318, 0.0029381425119936466, + 0.020084725692868233, 0.01667173206806183, -0.01258355937898159, + -0.0014171343063935637, 0.01907566748559475, 0.004488828592002392, + 0.052448805421590805, -0.048649996519088745, -0.014883620664477348, + 0.00984574481844902, 0.07379485666751862, 0.004614960867911577, + 0.004243983421474695, -0.0037023562472313643, -0.010498665273189545, + -0.027778800576925278, -0.01744336634874344, -0.03314313665032387, + 0.00989026203751564, 0.0028045906219631433, -0.032905708998441696, + 0.051499105989933014, 0.012858081609010696, 0.009118628688156605, + -0.030345967039465904, 0.007070832885801792, -0.02464032918214798, + 0.006173066794872284, -0.05717506259679794, 0.017821762710809708, + 0.03601450473070145, 0.0081911850720644, 0.009229921735823154, + 0.002574584446847439, 0.0038952643517404795, -0.02324545569717884, + -0.026257792487740517, 0.005045294761657715, 0.004362696316093206, + 0.014000694267451763, -0.020989911630749702, 0.012049351818859577, + 0.004703995771706104, 0.027734283357858658, -0.015172983519732952, + -0.00726374052464962, 0.0008458288502879441, -0.011277717538177967, + -0.011351913213729858, -0.010172205045819283, -0.016285916790366173, + 0.05145459249615669, -0.002567164832726121, -0.006314038299024105, + -0.031362444162368774, -0.014534901827573776, 0.007597620598971844, + -0.03720904886722565, 0.032245371490716934, 0.009927359409630299, + -0.006202745251357555, 0.02470710687339306, 0.004941421095281839, + 0.01927599497139454, 0.009422830305993557, 0.018956953659653664, + 0.013926498591899872, -0.0029900791123509407, 0.02242930419743061, + -0.027385564520955086, -0.013956177048385143, -0.047099314630031586, + -0.11699148267507553, 0.0574495866894722, -0.007597620598971844, + 0.03949427232146263, 0.02099733054637909, 0.01011284813284874, + 0.01009058952331543, 0.004674317315220833, 0.01161159761250019, + 0.006729532964527607, -0.012271937914192677, -0.004384954925626516, + 0.010342855006456375, 0.012323874980211258, 0.007545683532953262, + 0.005282720550894737, -0.037542931735515594, 0.012368392199277878, + -0.0074789077043533325, -0.026020366698503494, -0.007627299055457115, + 0.019335351884365082, 0.062405843287706375, -0.004058494698256254, + -0.009370893239974976, -0.06802986562252045 + ] + }, + "hiking": { + "prompt": "Photo of people hiking or walking together in nature", + "vector": [ + 0.002063393360003829, 0.0015020288992673159, -0.013541018590331078, + -0.007646692916750908, 0.026111029088497162, 0.0011834166944026947, + -0.02757512778043747, 0.007176360581070185, 0.019071215763688087, + -0.006736371666193008, -0.005340547300875187, -0.00493848929181695, + 0.0006903264438733459, 0.02783305011689663, 0.05169862136244774, + 0.5473757982254028, 0.010901088826358318, 0.008154954761266708, + 0.010863158851861954, -0.02394143119454384, 0.018168481066823006, + -0.0024426935706287622, -0.02306145429611206, -0.009588710032403469, + 0.01046110037714243, 0.00219235522672534, -0.029919203370809555, + -0.009262511506676674, 0.02251526340842247, 0.007934961467981339, + 0.028439931571483612, -0.0003868862404488027, 0.011454867199063301, + -0.01567268557846546, 0.031299855560064316, -0.016143018379807472, + 0.004331608768552542, -0.03375013545155525, 0.006872919853776693, + 0.0354645736515522, 0.0011682447511702776, -0.002533725695684552, + 0.0012441047001630068, -0.01750091277062893, -0.05769156292080879, + -0.022204237058758736, 0.03236947953701019, -0.047382187098264694, + 0.004976418800652027, 0.022613881155848503, 0.020360836759209633, + 0.008807350881397724, -0.0078970305621624, 0.029555076733231544, + -0.023433169350028038, -0.018456749618053436, -0.004399883095175028, + -0.04163958132266998, -0.007092914544045925, -0.0014640989247709513, + -0.04121476411819458, -0.0076922085136175156, -0.02539035677909851, + 0.026065511628985405, -0.026725493371486664, 0.015361659228801727, + -0.027309618890285492, 0.004134372808039188, 0.03704246133565903, + 0.015437520109117031, 0.004862628877162933, 0.014974773861467838, + -0.00012137607700424269, -0.03316601365804672, -0.03244534134864807, + 0.008905969560146332, -0.07830274105072021, 0.008481153286993504, + 0.06572514027357101, 0.028963366523385048, -0.021453222259879112, + -0.015354073606431484, -0.005780535284429789, -0.0006675684126093984, + 0.022181479260325432, 0.018722258508205414, -0.03245292976498604, + -0.0019192592008039355, 0.010499031282961369, 0.025853104889392853, + 0.005105380900204182, -0.01422375999391079, 0.00827633123844862, + -0.012342429719865322, -0.003967480733990669, 0.019033284857869148, + -0.08297571539878845, 0.03469838574528694, -0.013412055559456348, + 0.017940901219844818, -0.030533669516444206, -0.013503088615834713, + -0.01873743161559105, -0.06481482833623886, 0.0069411941803991795, + 0.020633932203054428, -0.05540059506893158, 0.014603059738874435, + 0.004559189081192017, -0.03791484981775284, -0.01018800400197506, + 0.0024730374570935965, -0.03339359536767006, -0.009528021328151226, + -0.023736609145998955, -0.013707909733057022, 0.009300441481173038, + -0.012926552444696426, 0.035995591431856155, -0.0003793002397287637, + 0.03805140033364296, -0.008162541314959526, 0.002298559295013547, + -0.014542371965944767, -0.012539665214717388, 0.00940664578229189, + 0.02364557795226574, -0.045121558010578156, -0.001008938648737967, + 0.00028068217216059566, 0.0036792121827602386, 0.02761305682361126, + 0.0065012057311832905, 0.020907029509544373, 0.06558859348297119, + 0.0028144079260528088, -0.01451202668249607, -0.005530197639018297, + -0.0006144663784652948, -0.0013199648819863796, 0.00856459978967905, + -0.0002958541736006737, -0.008079095743596554, 0.024707617238163948, + -0.007957719266414642, -0.003110261866822839, -0.002966127824038267, + 0.02645239792764187, -0.02149873785674572, 0.011318319477140903, + -0.04584981128573418, 0.014557542279362679, -0.005317789502441883, + 0.04957453906536102, -0.010324552655220032, 0.030859868973493576, + 0.024131080135703087, -0.002784063806757331, 0.0037702443078160286, + -0.024692445993423462, 0.0030950899235904217, 0.017834696918725967, + 0.009232167154550552, 0.012243811041116714, -0.0712856873869896, + 0.05383029207587242, 0.014056866988539696, -0.551009476184845, + 0.02320558950304985, -0.027984770014882088, -0.02834131382405758, + 0.05802535265684128, 0.0036033522337675095, -0.0293881818652153, + -0.04008444771170616, 0.0006068804068490863, -0.0058260513469576836, + -0.0210663340985775, 0.02102840505540371, 0.04547051340341568, + 0.006994296796619892, 0.03524458035826683, -0.0028523379005491734, + -0.01225898414850235, 0.042170602828264236, -0.015574067831039429, + -0.0283716581761837, -0.015346487052738667, 0.014436166733503342, + -0.012023817747831345, -0.0017220231238752604, 0.025845518335700035, + 0.03774796053767204, -0.050879333168268204, -0.026035169139504433, + -0.006121905520558357, 0.017622290179133415, -0.0962739810347557, + 0.00798806268721819, 0.011826581321656704, 0.04728356748819351, + 0.09593261033296585, 0.04386227950453758, 0.014322377741336823, + 0.03848380222916603, -0.01191002782434225, 0.017265748232603073, + -0.0007661865092813969, -0.013943076133728027, 0.01731126382946968, + 0.02589862048625946, -0.00002275801307405345, 0.0014640989247709513, + -0.009528021328151226, -0.011735549196600914, -0.011477624997496605, + 0.058154311031103134, -0.029744725674390793, -0.004635049030184746, + -0.020163599401712418, -0.02444210648536682, 0.009581124410033226, + 0.0047943550162017345, 0.05439165234565735, 0.020869098603725433, + 0.008041164837777615, 0.011098324321210384, -0.029911616817116737, + -0.023653162643313408, -0.08817972242832184, -0.010347310453653336, + -0.03296118974685669, 0.01143969502300024, -0.03907550871372223, + 0.0034440462477505207, -0.03758106753230095, -0.023251105099916458, + 0.02484416589140892, -0.011758306995034218, -0.034068744629621506, + -0.00598535779863596, 0.0004551603051368147, -0.03244534134864807, + -0.002495795488357544, 0.008503912016749382, 0.07327321916818619, + -0.026505500078201294, 0.013798942789435387, -0.08673837780952454, + -0.018540196120738983, -0.007790826726704836, -0.042034052312374115, + -0.055635757744312286, -0.029524730518460274, -0.0075860051438212395, + 0.007714967243373394, -0.0019799470901489258, -0.06055907532572746, + -0.0005386063130572438, 0.0027157897129654884, -0.04244369640946388, + 0.003686798270791769, 0.014519614167511463, 0.010984535329043865, + -0.0255344919860363, -0.021741488948464394, 0.005355719476938248, + 0.022014586254954338, -0.01018800400197506, -0.012941723689436913, + -0.0000910320522962138, 0.0032923261169344187, -0.0024654516018927097, + 0.02062634751200676, 0.03338600695133209, 0.005014349240809679, + 0.00467297900468111, 0.004202646669000387, 0.02229526825249195, + 0.00722187664359808, 0.014595472253859043, 0.009300441481173038, + 0.009376302361488342, -0.01757677271962166, -0.042360249906778336, + 0.0059019117616117, 0.009072861634194851, 0.009649398736655712, + -0.009679742157459259, 0.008132196962833405, -0.007889444939792156, + -0.005598471499979496, 0.03568456694483757, -0.0014489268651232123, + 0.007009468041360378, 0.028508204966783524, -0.03474390134215355, + -0.10007457435131073, 0.015376831404864788, -0.0013047928223386407, + -0.03398530185222626, 0.013578948564827442, 0.016431285068392754, + 0.017963659018278122, 0.027415819466114044, -0.01756918616592884, + -0.013442400842905045, 0.016363011673092842, 0.013783770613372326, + 0.04548568278551102, 0.05770673602819443, -0.000963422644417733, + -0.018100207671523094, -0.002875095698982477, 0.010051456280052662, + 0.05289721488952637, -0.03294602036476135, 0.016939548775553703, + -0.05000694468617439, -0.030321259051561356, 0.04122234880924225, + 0.008208057843148708, 0.03441770374774933, -0.008746663108468056, + 0.003239224199205637, -0.009725257754325867, -0.043528493493795395, + 0.0073811826296150684, 0.002905439818277955, 0.021946312859654427, + 0.020679449662566185, 0.0052874451503157616, -0.03149709105491638, + 0.05969427153468132, 0.0024882094003260136, -0.01367756724357605, + 0.024798648431897163, -0.026027580723166466, -0.018380889669060707, + -0.06441276520490646, 0.007593590300530195, -0.002723375800997019, + 0.026975832879543304, -0.010309380479156971, 0.004232990555465221, + -0.0185477826744318, 0.010787298902869225, 0.006175007671117783, + 0.00040964424260891974, 0.01695472002029419, -0.00923975370824337, + 0.002556483494117856, 0.02156701311469078, -0.07915237545967102, + 0.01619611866772175, 0.004278506617993116, -0.019078800454735756, + 0.02157459780573845, -0.0014792709844186902, -0.0033302560914307833, + 0.020641518756747246, -0.019526375457644463, -0.0314212329685688, + -0.013078272342681885, -0.005461923312395811, -0.00219235522672534, + -0.027734432369470596, -0.02193872444331646, 0.01945810206234455, + 0.020239459350705147, -0.008344604633748531, 0.019518790766596794, + -0.04682840779423714, 0.006834989879280329, 0.007237048353999853, + -0.01417065691202879, 0.028690271079540253, -0.014921671710908413, + -0.061385948210954666, -0.015703029930591583, 0.003117847954854369, + -0.007358424365520477, -0.02138494700193405, -0.007343252655118704, + 0.05872325971722603, -0.01699265092611313, 0.020307734608650208, + -0.016605764627456665, 0.02196907065808773, 0.006599824409931898, + 0.02062634751200676, 0.0405016764998436, -0.0021771832834929228, + 0.04533396288752556, 0.015134080313146114, -0.020535314455628395, + -0.006250868085771799, -0.015217525884509087, 0.02823510952293873, + -0.1144576445221901, 0.023304205387830734, 0.03866586834192276, + 0.0056212292984128, 0.012175537645816803, -0.02510209009051323, + -0.012592768296599388, -0.002336489502340555, 0.06109009310603142, + -0.004953661002218723, 0.009482505731284618, -0.03818794712424278, + -0.008177713491022587, 0.00503710750490427, -0.0011758307227864861, + 0.014542371965944767, 0.0022378715220838785, -0.0035274922847747803, + 0.0026854455936700106, 0.023380065336823463, 0.02768133208155632, + 0.11998025327920914, -0.07625452429056168, -0.06897195428609848, + 0.02488209493458271, 0.015012702904641628, -0.020057396963238716, + -0.010756954550743103, 0.01931396685540676, 0.03437977284193039, + -0.002108909422531724, -0.004710908979177475, 0.012069333344697952, + 0.008147369138896465, -0.027150310575962067, -0.06366175413131714, + 0.01267621386796236, 0.023304205387830734, 0.03529009595513344, + -0.016309909522533417, -0.0025792415253818035, -0.004415054805576801, + 0.00925492588430643, 0.007745311129838228, -0.026497915387153625, + 0.0015247869305312634, 0.020679449662566185, -0.019943606108427048, + 0.022644223645329475, 0.0021013233345001936, -0.0057729496620595455, + -0.057418469339609146, 0.016567833721637726, -0.040615469217300415, + 0.021111849695444107, -0.016051985323429108, -0.017409881576895714, + -0.027529612183570862, 0.00851149670779705, -0.016271980479359627, + -0.01257000956684351, 0.01160658709704876, -0.03044263832271099, + -0.011447281576693058, 0.03321152925491333, 0.010286622680723667, + 0.05867016315460205, -0.0005006763385608792, 0.005651573650538921, + 0.02270491234958172, 0.019670508801937103, 0.009103205054998398, + 0.0033909440971910954, 0.007828757166862488, 0.012486563995480537, + -0.06490585952997208, 0.014314791187644005, -0.02128632925450802, + 0.01449685450643301, 0.02833372727036476, -0.006053632125258446, + -0.001084798714146018, 0.03882517293095589, 0.021870451048016548, + -0.016522318124771118, 0.0028826817870140076, -0.049089036881923676, + -0.06481482833623886, 0.0018206412205472589, 0.023296620696783066, + 0.019404999911785126, 0.02746892161667347, 0.031079862266778946, + 0.01899535581469536, -0.040478918701410294, 0.04027410224080086, + 0.01311620231717825, -0.001866157166659832, -0.055635757744312286, + -0.016234049573540688, 0.0015323730185627937, -0.021407704800367355, + -0.009050103835761547, -0.007494972553104162, 0.011614173650741577, + -0.00951285008341074, -0.018889151513576508, -0.01571820117533207, + 0.012501736171543598, 0.06539894640445709, -0.006281211972236633, + 0.011727963574230671, -0.04220853000879288 + ] + }, + "feast": { + "prompt": "Photo of people having a big feast together", + "vector": [ + -0.029508370906114578, 0.006410595495253801, -0.022999616339802742, + -0.04990297555923462, 0.017683882266283035, -0.010110467672348022, + -0.023958561941981316, 0.004115164279937744, -0.03147157281637192, + -0.0791093111038208, -0.01019352674484253, 0.03028610162436962, + -0.005285532213747501, 0.016543716192245483, 0.01726858876645565, + 0.5275111794471741, -0.016181280836462975, -0.005934897810220718, + -0.017427153885364532, -0.01871078461408615, -0.007905646227300167, + -0.03548102453351021, 0.024102026596665382, 0.005323286168277264, + -0.00561021501198411, -0.002605012385174632, -0.01120532862842083, + -0.006410595495253801, 0.030542826279997826, 0.03679485619068146, + -0.009483755566179752, -0.035435717552900314, 0.0168155450373888, + 0.012134072370827198, -0.004001902882009745, 0.04106858745217323, + -0.013289338909089565, -0.009597016498446465, -0.026314400136470795, + 0.014791940338909626, 0.07266851514577866, -0.017094921320676804, + -0.00034733497886918485, 0.020719286054372787, -0.03684015944600105, + -0.051322516053915024, -0.026714589446783066, -0.0372932069003582, + -0.004938197322189808, 0.017162878066301346, 0.013908501714468002, + 0.011575316078960896, 0.03738381713628769, -0.004032106138765812, + -0.01874098740518093, -0.011099617928266525, -0.01941300556063652, + 0.003224174724891782, -0.03239276260137558, 0.008056661114096642, + -0.029923664405941963, 0.008396445773541927, -0.012330392375588417, + -0.0709695965051651, -0.04053248465061188, 0.006886293180286884, + -0.005647968500852585, -0.04843812808394432, 0.004855139181017876, + 0.009838640689849854, 0.00040774105582386255, -0.025453614071011543, + -0.017955707386136055, 0.02265983261168003, 0.029281847178936005, + -0.01099390722811222, -0.06056464836001396, 0.009732929989695549, + 0.09662707895040512, 0.01938280090689659, 0.00030203041387721896, + -0.02985570766031742, -0.011424301192164421, -0.007709326688200235, + 0.019480960443615913, 0.047864269465208054, 0.0035111038014292717, + -0.006252029910683632, 0.0017517763189971447, -0.007497905287891626, + -0.016649426892399788, 0.015033564530313015, -0.02813413366675377, + 0.0017895301571115851, -0.012141622602939606, 0.022909006103873253, + -0.13033367693424225, 0.0323852114379406, -0.02679009921848774, + 0.015373348258435726, -0.0010118018835783005, 0.024849552661180496, + -0.010050062090158463, 0.05398038774728775, 0.021149680018424988, + 0.008532359264791012, -0.04978971555829048, 0.012164275161921978, + 0.03536776453256607, -0.009438450448215008, 0.006908946204930544, + -0.028670238330960274, 0.04793977737426758, -0.03107893094420433, + -0.00043039332376793027, -0.09445246309041977, 0.041287556290626526, + -0.03239276260137558, 0.013568716123700142, 0.031305454671382904, + -0.010352092795073986, -0.00006795684748794883, -0.019397905096411705, + -0.045115794986486435, 0.005270430818200111, -0.006206724792718887, + 0.04941217601299286, -0.031645238399505615, 0.0459916815161705, + 0.006365290842950344, -0.017872650176286697, 0.029508370906114578, + -0.00622937735170126, 0.005776331759989262, -0.014505011029541492, + -0.014248285442590714, -0.009763133712112904, -0.009702727198600769, + 0.01652861386537552, 0.03707423433661461, -0.00672017689794302, + 0.025831151753664017, -0.009242130443453789, -0.044255007058382034, + 0.006395494099706411, -0.018363449722528458, 0.00662201689556241, + 0.030399363487958908, -0.015713132917881012, 0.018167128786444664, + -0.015833944082260132, 0.0032543777488172054, -0.06430982798337936, + 0.02250126749277115, 0.014172777533531189, 0.03633425757288933, + 0.02984815463423729, -0.01769143156707287, 0.0005814085598103702, + -0.01925443857908249, -0.037919919937849045, -0.012254884466528893, + -0.008396445773541927, 0.009959452785551548, 0.057582102715969086, + -0.04155183583498001, 0.013508310541510582, -0.5311808586120605, + -0.0006191623397171497, 0.002084009815007448, -0.008177473209798336, + 0.04822670668363571, -0.02510627917945385, 0.027484769001603127, + 0.003480900777503848, -0.0014119921252131462, 0.040977977216243744, + -0.010004756972193718, -0.01550926174968481, -0.002846636576578021, + 0.023565921932458878, 0.016400251537561417, 0.014384198933839798, + 0.009068462997674942, 0.04809079319238663, -0.013712181709706783, + 0.0087739834561944, -0.02348286472260952, -0.010978804901242256, + -0.02327144332230091, 0.024630580097436905, 0.004923095460981131, + -0.0450025349855423, -0.018431406468153, -0.02188965491950512, + -0.029259197413921356, -0.020983561873435974, 0.023807547986507416, + -0.02648051828145981, 0.05487137287855148, 0.0007550760637968779, + 0.003443146590143442, -0.00040019029984250665, -0.02216148190200329, + -0.008992955088615417, -0.01047290489077568, -0.0007324237376451492, + -0.027545172721147537, 0.024086926132440567, -0.008917448110878468, + 0.01448235847055912, 0.0016838194569572806, -0.004462499171495438, + -0.013025062158703804, 0.022674933075904846, 0.023867953568696976, + -0.09708012640476227, -0.013727283105254173, -0.031886860728263855, + 0.008034009486436844, -0.0067352778278291225, 0.05508279800415039, + 0.010352092795073986, 0.02363388054072857, 0.004024555440992117, + -0.010676775127649307, 0.0008456851937808096, -0.016800440847873688, + 0.00014346445095725358, -0.0004152918409090489, 0.01708737015724182, + 0.03860703855752945, -0.011877345852553844, -0.02216903120279312, + -0.02170843631029129, 0.056842122226953506, 0.003428045427426696, + -0.005051458720117807, -0.0659709945321083, -0.0037753803189843893, + -0.004175570793449879, 0.011620620265603065, -0.0223502516746521, + 0.011507358402013779, -0.07293279469013214, 0.0331176333129406, + 0.006810786202549934, -0.00946110300719738, -0.03183400630950928, + 0.0026276647113263607, -0.021610276773571968, -0.04296382516622543, + -0.059968139976263046, 0.02210862748324871, -0.022448411211371422, + 0.00127607851754874, -0.030225694179534912, -0.02345266193151474, + 0.008215227164328098, 0.03343476727604866, -0.016249235719442368, + -0.030784450471401215, 0.017955707386136055, 0.014965606853365898, + 0.009159073233604431, -0.015214783139526844, 0.044353168457746506, + -0.012813640758395195, -0.011673475615680218, 0.004084961488842964, + 0.010276584886014462, 0.03187175840139389, 0.006093463860452175, + 0.0021972714457660913, 0.004681471269577742, -0.02412468008697033, + -0.005330836866050959, 0.008260532282292843, 0.013968906365334988, + 0.030852409079670906, 0.012488958425819874, 0.03156217932701111, + 0.028957165777683258, -0.0005889593157917261, 0.01711002178490162, + 0.026835402473807335, 0.019216684624552727, 0.004704123828560114, + -0.029644286260008812, -0.006319986190646887, -0.041868966072797775, + -0.017321443185210228, -0.015086418949067593, -0.04332626610994339, + 0.011439401656389236, 0.0019707484170794487, -0.012889147736132145, + -0.047192253172397614, 0.017525315284729004, 0.016543716192245483, + 0.030225694179534912, -0.014301139861345291, 0.02335450053215027, + -0.005693273153156042, 0.039082735776901245, -0.011937752366065979, + 0.02835310623049736, 0.019110973924398422, -0.002778679830953479, + -0.010654122568666935, -0.05033336952328682, -0.018997713923454285, + -0.03303457424044609, 0.0017593271331861615, 0.015071317553520203, + 0.05412385240197182, -0.0390600822865963, 0.01020862814038992, + -0.04464764520525932, 0.005149618722498417, 0.021353550255298615, + 0.014633373357355595, -0.008940099738538265, 0.050348468124866486, + 0.003941496834158897, 0.003722524968907237, -0.026095427572727203, + 0.006168971303850412, 0.018514463678002357, 0.009989656507968903, + 0.003216624027118087, -0.0018952408572658896, 0.010231280699372292, + 0.09087339788675308, -0.020960910245776176, -0.004696573130786419, + 0.01754041574895382, -0.02033419720828533, -0.027296001091599464, + -0.018869351595640182, -0.036387115716934204, 0.028859006240963936, + 0.058525945991277695, -0.015373348258435726, 0.00786789320409298, + -0.03333660960197449, 0.024056723341345787, -0.004628615919500589, + -0.06604650616645813, 0.022116176784038544, -0.010404948145151138, + 0.0031713193748146296, 0.019692381843924522, -0.08060436695814133, + 0.015637625008821487, 0.017261039465665817, -0.11375976353883743, + 0.008041559718549252, 0.00019631977193057537, -0.05997568741440773, + 0.041045933961868286, -0.005315735470503569, -0.002378489589318633, + 0.01956401951611042, 0.05740842968225479, 0.005942448507994413, + 0.022486163303256035, 0.002099111443385482, 0.02795291505753994, + -0.03366883844137192, -0.014572967775166035, -0.0296065304428339, + 0.018793843686580658, -0.016490861773490906, -0.0028390861116349697, + 0.021549871191382408, 0.04615779593586922, -0.019088322296738625, + -0.12028361111879349, -0.010571064427495003, -0.032709892839193344, + -0.026865605264902115, -0.02486465498805046, 0.006221826653927565, + 0.013802791014313698, -0.0009060912416316569, 0.01171122957020998, + 0.0067352778278291225, 0.05201718956232071, 0.01818978041410446, + -0.09494326263666153, -0.004756979178637266, -0.014452156610786915, + 0.014799490571022034, 0.005647968500852585, 0.058926135301589966, + 0.006916496902704239, 0.014051965437829494, -0.012874046340584755, + -0.06671097129583359, 0.023679183796048164, 0.028405960649251938, + -0.01400666031986475, -0.03927905485033989, -0.015849046409130096, + 0.005126966163516045, 0.04285811632871628, 0.04158203676342964, + 0.014505011029541492, -0.053074296563863754, -0.00996700394898653, + 0.021210087463259697, 0.008940099738538265, 0.02851167321205139, + 0.01449745986610651, 0.008185024373233318, 0.00274092610925436, + 0.022425759583711624, 0.009408247657120228, -0.007052409928292036, + 0.03719504550099373, -0.02506852336227894, 0.016490861773490906, + -0.011046762578189373, 0.052311670035123825, 0.018756089732050896, + -0.009415797889232635, 0.0008683374035172164, 0.022403106093406677, + -0.03511858731508255, 0.0030807103030383587, -0.01668718084692955, + -0.00672017689794302, -0.015562117099761963, -0.005376141518354416, + 0.021595174446702003, 0.027409261092543602, -0.00037753803189843893, + -0.004605963826179504, 0.016294540837407112, -0.030135085806250572, + 0.0011477156076580286, -0.03647017478942871, -0.044413574039936066, + 0.010835341177880764, 0.02777169644832611, -0.00030203041387721896, + 0.04723755642771721, -0.004636167082935572, -0.0037602786906063557, + -0.06221826747059822, 0.003178870305418968, -0.011628171429038048, + 0.004175570793449879, -0.02207842469215393, -0.02427569404244423, + 0.007361991330981255, 0.01446725707501173, 0.02302226796746254, + -0.004930646624416113, -0.0018423855071887374, -0.008653171360492706, + 0.0005436547799035907, 0.0017593271331861615, 0.012300188653171062, + 0.026533372700214386, -0.03156972676515579, 0.021655581891536713, + -0.05373876169323921, -0.005013705231249332, -0.009204376488924026, + 0.00019631977193057537, 0.04528946056962013, 0.02777169644832611, + 0.022116176784038544, -0.024577725678682327, -0.02348286472260952, + -0.004855139181017876, 0.007445049937814474, 0.010246382094919682, + -0.0012911800295114517, -0.0016309642232954502, 0.0021897205151617527, + -0.044776007533073425, -0.06371331959962845, -0.00727138202637434, + -0.09274598956108093, 0.03334415704011917, 0.030724044889211655, + -0.006418146193027496, 0.02210862748324871, -0.019345048815011978, + -0.019178932532668114, -0.041159193962812424, -0.009159073233604431, + 0.008290735073387623, -0.014746634289622307, 0.000823032867629081, + 0.023520618677139282, -0.008502155542373657, 0.01005761232227087, + -0.0026654184330254793, 0.0026427661068737507, 0.006297334562987089, + 0.02388305403292179, -0.011371445842087269, -0.014286038465797901, + 0.016279440373182297, 0.045025184750556946, 0.00844930112361908, + 0.024653233587741852, -0.13621573150157928 + ] + }, + "selfies": { + "prompt": "Happy and nostalgic selfie with people, clearly taken from the front camera of a phone", + "vector": [ + 0.015917416661977768, 0.050739727914333344, -0.02502211555838585, + -0.087443046271801, 0.03248291090130806, 0.0026871508453041315, + -0.00765837961807847, 0.007144659757614136, 0.03526490181684494, + 0.001857295399531722, -0.003943788819015026, -0.007460794877260923, + -0.03395294025540352, -0.0075793457217514515, -0.004188793711364269, + 0.4699036478996277, 0.030467547476291656, -0.014897879213094711, + 0.0670839250087738, -0.05165652185678482, -0.015071753412485123, + -0.02868928574025631, 0.025488415732979774, -0.010851346887648106, + 0.017822131514549255, 0.01167329866439104, -0.01135716401040554, + -0.009808100759983063, 0.06773991137742996, 0.01942651905119419, + 0.02820718102157116, -0.041832614690065384, -0.005722050555050373, + 0.01917361095547676, 0.0193316787481308, -0.03930353373289108, 0.0, + 0.0002133913803845644, 0.0028136048931628466, -0.049388252198696136, + -0.035889267921447754, 0.004259924404323101, -0.04313667118549347, + 0.04614786058664322, 0.04571317881345749, -0.05501545965671539, + -0.0005216234130784869, -0.025361960753798485, 0.03127369284629822, + -0.012866709381341934, -0.056959692388772964, 0.07071158289909363, + 0.015150788240134716, -0.027337808161973953, -0.055307887494564056, + -0.046590451151132584, 0.00572995375841856, 0.042409561574459076, + -0.03867916390299797, 0.011823463253676891, -0.01588580198585987, + 0.006188350263983011, -0.03316260129213333, -0.004797354806214571, + -0.08805950731039047, 0.010337627492845058, 0.017490191385149956, + 0.008749046362936497, 0.025891486555337906, 0.049546319991350174, + 0.014629164710640907, 0.054177701473236084, -0.0517592653632164, + 0.015150788240134716, -0.008662109263241291, -0.001414705766364932, + -0.069162517786026, -0.012455734424293041, 0.06672036647796631, + 0.008843887597322464, 0.048005156219005585, -0.04151647910475731, + 0.019766364246606827, 0.020738480612635612, 0.014992720447480679, + 0.04920647293329239, -0.0025211796164512634, 0.019797977060079575, + 0.035960398614406586, 0.0034300689585506916, 0.0030586100183427334, + 0.027100706472992897, 0.024808725342154503, 0.012179115787148476, + -0.013111715205013752, 0.013894150033593178, -0.003643460338935256, + 0.033249542117118835, -0.04278102144598961, -0.020232664421200752, + -0.007176273502409458, 0.01348317414522171, -0.043500229716300964, + -0.01609129086136818, -0.0025369864888489246, 0.0075872489251196384, + -0.042788922786712646, 0.04044952243566513, -0.01958458684384823, + 0.003667170647531748, -0.01606758125126362, -0.023465149104595184, + 0.015032238326966763, -0.059915561228990555, 0.008970341645181179, + 0.03035689890384674, 0.010685376822948456, -0.05084247514605522, + -0.016533881425857544, 0.005737856961786747, 0.03273581713438034, + -0.010369240306317806, 0.014692391268908978, -0.0033431316260248423, + 0.020959775894880295, 0.0011143771698698401, 0.050478916615247726, + -0.04680384323000908, -0.01737954281270504, -0.012890420854091644, + -0.021394461393356323, 0.014945301227271557, -0.023710153996944427, + -0.009657936170697212, 0.008053549565374851, 0.027456358075141907, + 0.0069075580686330795, 0.02016153372824192, 0.0330914705991745, + -0.07644153386354446, -0.02246141992509365, -0.00044258954585529864, + -0.0005216234130784869, -0.026428917422890663, -0.0035091026220470667, + -0.0034853925462812185, -0.0336921289563179, -0.022382386028766632, + 0.031281597912311554, -0.08310408890247345, 0.009657936170697212, + 0.02995382808148861, -0.017901165410876274, 0.0012171212583780289, + 0.022777553647756577, -0.0060223788022994995, -0.0018414886435493827, + -0.01603596843779087, -0.0593465156853199, -0.04250440001487732, + 0.027709266170859337, 0.03439553081989288, -0.03536764532327652, + 0.011199096217751503, 0.03919288516044617, -0.008986148051917553, + 0.017110828310251236, -0.4741714596748352, 0.05406705290079117, + 0.017442768439650536, -0.004923808388411999, 0.05942555144429207, + 0.027914753183722496, -0.009246960282325745, 0.0069154612720012665, + -0.04851888120174408, 0.02397886849939823, 0.0008693723357282579, + 0.022121572867035866, 0.13283218443393707, -0.015245628543198109, + -0.006488678976893425, 0.01965571753680706, -0.012218632735311985, + 0.04632173851132393, 0.057386476546525955, 0.002450049389153719, + -0.02909236028790474, 0.004354765173047781, -0.059259574860334396, + 0.026958445087075233, -0.019473940134048462, 0.0340556837618351, + -0.03817334771156311, 0.004117663484066725, -0.018738925457000732, + 0.0018731020390987396, -0.018154073506593704, -0.03981725126504898, + 0.0010432468261569738, 0.026326173916459084, 0.04975971207022667, + -0.059109412133693695, -0.029021229594945908, -0.02719554677605629, + -0.012756062671542168, -0.02954285219311714, 0.03445085510611534, + 0.050146978348493576, 0.05016278102993965, -0.008511945605278015, + -0.006512389052659273, 0.0041097598150372505, -0.018936509266495705, + -0.024397749453783035, 0.004615576937794685, 0.032064031809568405, + 0.039959512650966644, 0.02782781794667244, 0.0013198652304708958, + 0.015972739085555077, 0.015332565642893314, 0.016280973330140114, + 0.011570555157959461, 0.022659003734588623, 0.010179559700191021, + -0.011910400353372097, 0.015672411769628525, 0.01436835341155529, + -0.017316315323114395, -0.02405790239572525, -0.006046089343726635, + 0.0014779329067096114, -0.07849641144275665, 0.0059828623197972775, + 0.03033319115638733, 0.016968566924333572, -0.03571539372205734, + -0.001509546535089612, -0.020951872691512108, -0.012171212583780289, + 0.004180890507996082, -0.01209217868745327, 0.02031169831752777, + -0.041706159710884094, 0.049570027738809586, 0.015538053587079048, + -0.05229669809341431, -0.019845400005578995, -0.04116082563996315, + 0.011025221087038517, -0.08250343799591064, 0.008551462553441525, + 0.003888465464115143, -0.02114945650100708, -0.025899391621351242, + -0.03126579150557518, -0.061259131878614426, -0.04042581096291542, + 0.015893707051873207, -0.07852012664079666, -0.01596483774483204, + 0.013467367738485336, 0.009444545023143291, 0.040868401527404785, + -0.028807837516069412, -0.027914753183722496, -0.035280708223581314, + 0.016842111945152283, 0.01143619790673256, -0.0288157407194376, + 0.022295447066426277, 0.016178227961063385, -0.031811121851205826, + -0.02838895656168461, 0.025899391621351242, 0.0017466479912400246, + 0.008662109263241291, -0.00922324974089861, 0.001983749447390437, + 0.027764590457081795, -0.026247140020132065, 0.000545333547051996, + -0.061132680624723434, -0.014905783347785473, 0.03196919336915016, + -0.0200824998319149, 0.00834597460925579, -0.02823088876903057, + 0.004931711591780186, -0.03958805650472641, -0.03649783134460449, + -0.021916085854172707, 0.03401616960763931, 0.002126010600477457, + 0.014921589754521847, -0.033557772636413574, 0.08977454900741577, + 0.03425326943397522, 0.003366841934621334, 0.005113489460200071, + -0.015617088414728642, -0.025306636467576027, 0.03321792557835579, + -0.007460794877260923, 0.0021655273158103228, 0.006828524172306061, + 0.020201051607728004, -0.016612913459539413, -0.04017290472984314, + -0.04703304171562195, -0.018146172165870667, -0.008290650323033333, + 0.0038252382073551416, 0.018960220739245415, 0.04915114864706993, + -0.049799226224422455, 0.07689203321933746, -0.027377326041460037, + 0.015553861856460571, 0.022303352132439613, -0.06472081691026688, + -0.022706422954797745, 0.036545250564813614, 0.09203491359949112, + -0.020849129185080528, 0.007049818988889456, 0.06066638231277466, + -0.02196350507438183, 0.008496138267219067, 0.03311518207192421, + -0.00007903384539531544, 0.021694790571928024, 0.04487541690468788, + 0.007895481772720814, -0.03597620874643326, -0.0013751889346167445, + 0.015648702159523964, -0.0345061756670475, 0.01453432347625494, + -0.01897602714598179, -0.031155141070485115, 0.026492144912481308, + -0.0359208844602108, 0.008164196275174618, -0.042354241013526917, + -0.021268006414175034, -0.014613358303904533, -0.13114877045154572, + 0.018509726971387863, 0.004489122424274683, -0.013570111244916916, + 0.01238460373133421, -0.030949654057621956, 0.025646483525633812, + 0.015846285969018936, -0.09424786269664764, 0.024674367159605026, + -0.044029753655195236, 0.01856505125761032, -0.01834375597536564, + 0.02671344019472599, -0.005263654049485922, 0.0069075580686330795, + 0.020398635417222977, 0.013720275834202766, 0.0563037134706974, + 0.044614605605602264, -0.000987923122011125, -0.008369684219360352, + 0.036972030997276306, -0.03273581713438034, 0.01824101060628891, + -0.03617379069328308, 0.014423677697777748, -0.02711651287972927, + 0.07519280165433884, -0.03147917985916138, 0.05080295726656914, + 0.0008061452535912395, -0.021220587193965912, -0.0074449884705245495, + 0.007531925570219755, -0.0281518567353487, 0.03056238777935505, + -0.03332066908478737, -0.017387446016073227, -0.004196696914732456, + 0.06400161236524582, -0.10398483276367188, -0.061053644865751266, + -0.07527974247932434, -0.008646302856504917, 0.022255931049585342, + 0.01205266173928976, 0.026223430410027504, -0.03162934258580208, + -0.031613539904356, 0.0017071310430765152, -0.08489025384187698, + 0.0036592669785022736, -0.07139917463064194, 0.0009009858476929367, + -0.0031139336060732603, 0.043626684695482254, 0.003651363542303443, + 0.019900722429156303, 0.03483021631836891, -0.007500311825424433, + 0.02757490798830986, -0.04568946361541748, 0.028302019461989403, + -0.015119175426661968, -0.008203713223338127, -0.012835096567869186, + 0.042615048587322235, -0.039279818534851074, 0.02332288771867752, + -0.0243898443877697, -0.03396874666213989, 0.052881546318531036, + -0.07279808074235916, -0.03365261107683182, 0.02646053023636341, + 0.039121754467487335, 0.015893707051873207, 0.007421278394758701, + 0.025788743048906326, 0.020675254985690117, 0.010637955740094185, + 0.011657492257654667, -0.03491715341806412, -0.03268839791417122, + 0.0022682715207338333, 0.019813785329461098, 0.01329349260777235, + -0.004133470356464386, 0.0031139336060732603, 0.012961551547050476, + -0.023204337805509567, -0.032166775315999985, -0.06231028214097023, + -0.08763272315263748, -0.0237259604036808, -0.0020074595231562853, + -0.034245364367961884, -0.04824225977063179, -0.017703581601381302, + -0.015640798956155777, -0.023836607113480568, -0.019015545025467873, + -0.014605454169213772, 0.06023959815502167, -0.028136050328612328, + -0.0054454319179058075, 0.009989878162741661, 0.0352570004761219, + -0.0196320079267025, 0.023267565295100212, 0.017624547705054283, + -0.00889130774885416, -0.020422345027327538, 0.010313916951417923, + 0.011823463253676891, -0.06701279431581497, 0.0280333049595356, + -0.03893997520208359, -0.01917361095547676, -0.027179740369319916, + -0.043421193957328796, -0.02129962295293808, -0.027582813054323196, + -0.005413818638771772, -0.0025923100765794516, 0.006164640188217163, + -0.02005879022181034, 0.03063351847231388, 0.05219395086169243, + -0.021995117887854576, 0.04875598102807999, -0.02876041643321514, + -0.005168813746422529, 0.04773644357919693, 0.015134981833398342, + 0.000940502795856446, 0.02046186290681362, -0.08118356764316559, + 0.013554304838180542, -0.005737856961786747, 0.02631036750972271, + 0.04408508166670799, 0.027203449979424477, -0.02306997962296009, + 0.003619750263169408, -0.03498038277029991, 0.014012700878083706, + -0.0035011994186788797, -0.008021934889256954, + -0.000023710153982392512, 0.0044337990693748, 0.012487347237765789, + -0.01917361095547676, 0.015142884105443954, -0.02519599162042141, + 0.005413818638771772, -0.0033826485741883516, 0.0006796910893172026, + 0.047341275960206985, 0.039556439965963364, -0.018130365759134293, + -0.027353614568710327, -0.05560821294784546 + ] + }, + "posing": { + "prompt": "Photo of people posing together in a funny manner for the camera", + "vector": [ + 0.010862172581255436, -0.002259705914184451, -0.019589310511946678, + -0.02353210747241974, 0.011734886094927788, -0.021841226145625114, + 0.0055947196669876575, 0.027217766270041466, 0.024350278079509735, + -0.04710317775607109, 0.01493743434548378, -0.04109547659754753, + 0.028659304603934288, 0.005968740209937096, -0.009934913367033005, + 0.568651020526886, 0.019573727622628212, -0.05230829492211342, + 0.009732319042086601, -0.033996883779764175, -0.004534995649009943, + -0.017352983355522156, -0.023680157959461212, -0.019028281792998314, + 0.007347939535975456, 0.011236192658543587, -0.006218086928129196, + -0.007293395232409239, 0.0030700829811394215, -0.0027506074402481318, + 0.003638905705884099, -0.00935829896479845, -0.007698584347963333, + 0.03275015205144882, 0.013737453147768974, -0.009693358093500137, + -0.007371315732598305, 0.004075262695550919, 0.0011064766440540552, + 0.02505156584084034, 0.02505156584084034, 0.003989549353718758, + -0.0027817757800221443, -0.013114085420966148, 0.01835816167294979, + -0.022285373881459236, -0.02734243869781494, -0.021069807931780815, + 0.038516294211149216, -0.008438833057880402, -0.03540725260972977, + 0.041056517511606216, 0.008960902690887451, -0.00017921804101206362, + -0.014890681952238083, -0.04561488702893257, 0.01084658782929182, + -0.00024155476421583444, -0.0013480314519256353, + -0.0008415456395596266, -0.010643993504345417, 0.0029609936755150557, + -0.037277352064847946, -0.016129624098539352, 0.0026804786175489426, + 0.002703854814171791, -0.009732319042086601, -0.0028441124595701694, + -0.00356877688318491, 0.07399367541074753, 0.011415409855544567, + 0.015599762089550495, -0.0187165979295969, 0.012638768181204796, + -0.009264794178307056, 0.01648026891052723, -0.05869780480861664, + -0.005649264436215162, 0.03279690444469452, 0.028955401852726936, + -0.011197231709957123, -0.027981389313936234, 0.004449282772839069, + -0.010519320145249367, -0.01870880462229252, -0.007340147625654936, + -0.0010207636514678597, -0.0014804968377575278, -0.008002475835382938, + 0.005890819244086742, 0.005602512042969465, -0.00751157384365797, + 0.02130357176065445, -0.015358206816017628, 0.02491910010576248, + 0.022565890103578568, -0.020968511700630188, -0.010106339119374752, + 0.0006467433995567262, 0.006779117044061422, -0.01658935658633709, + 0.0009506348869763315, -0.019472431391477585, 0.01172709371894598, + -0.0008415456395596266, 0.03326442837715149, -0.038874730467796326, + -0.008968694135546684, -0.00034285191213712096, + -0.0005688224919140339, -0.020898383110761642, -0.02009579725563526, + 0.08457533270120621, -0.007698584347963333, 0.00875051598995924, + -0.0446486696600914, 0.011633588932454586, -0.01245955005288124, + -0.00289086508564651, 0.004145391285419464, 0.05244075879454613, + -0.011571251787245274, 0.015389375388622284, -0.00446486659348011, + -0.01843608170747757, -0.017368566244840622, 0.02174772135913372, + -0.013371225446462631, 0.0169244185090065, -0.012412797659635544, + -0.01758674345910549, 0.005228491500020027, -0.03711371868848801, + 0.015623139217495918, 0.05841728672385216, 0.001667506992816925, + -0.026095706969499588, 0.0027272312436252832, 0.002477884292602539, + -0.022908741608262062, -0.023773664608597755, 0.022191869094967842, + 0.0031480039469897747, -0.013238758780062199, 0.019519183784723282, + 0.02527753636240959, 0.005290828179568052, -0.014056929387152195, + 0.0027272312436252832, -0.0545835867524147, -0.009023238904774189, + 0.028893064707517624, -0.04298895597457886, 0.0009194665472023189, + -0.027482695877552032, -0.008134940639138222, -0.0074726128950715065, + -0.011220607906579971, -0.037963055074214935, -0.029111245647072792, + -0.020578907802700996, 0.019550351426005363, -0.02579960599541664, + 0.006779117044061422, -0.02009579725563526, 0.037822797894477844, + -0.018038686364889145, -0.5727107524871826, 0.025674933567643166, + 0.01623092219233513, -0.028682678937911987, 0.05233946070075035, + 0.015288078226149082, 0.0012545263161882758, 0.0124439662322402, + -0.015132236294448376, 0.012771233916282654, 0.0011454371269792318, + -0.004254480358213186, 0.03370078653097153, -0.003444103291258216, + 0.038072146475315094, 0.020789293572306633, -0.02684374526143074, + 0.021202275529503822, 0.008197277784347534, -0.022978870198130608, + -0.024404821917414665, 0.027825549244880676, 0.0008415456395596266, + 0.02039968967437744, -0.015420544892549515, 0.054692670702934265, + -0.044461656361818314, -0.005851858761161566, 0.007231058552861214, + -0.021326947957277298, 0.00887518934905529, 0.0011376449838280678, + 0.0008882981492206454, 0.05418618768453598, 0.03594490885734558, + -0.05126415193080902, -0.014914058148860931, 0.006646651774644852, + -0.009334922768175602, -0.025900904089212418, -0.006108997855335474, + 0.025791816413402557, 0.03403584286570549, -0.0022674978245049715, + -0.0010129715083166957, 0.01477380096912384, -0.026391804218292236, + -0.022830819711089134, 0.04309804365038872, -0.012825778685510159, + 0.020984094589948654, 0.014984187670052052, 0.012217995710670948, + 0.002127240179106593, 0.03755787014961243, -0.006911583244800568, + 0.021342530846595764, 0.0182179044932127, 0.00843103975057602, + -0.0045583718456327915, 0.0017532199854031205, -0.009038823656737804, + -0.03983315825462341, -0.02408534660935402, -0.03221249580383301, + 0.006171334534883499, -0.016293257474899292, -0.0228230282664299, + 0.01149333082139492, -0.007223266176879406, 0.007893386296927929, + -0.015116652473807335, -0.0013012788258492947, -0.002251913771033287, + 0.0013090709690004587, -0.06694962829351425, 0.014999771490693092, + -0.05226153880357742, 0.015124444849789143, -0.0295398086309433, + -0.009015446528792381, -0.01918412186205387, -0.02356327697634697, + 0.017758170142769814, -0.048490170389413834, 0.0064206812530756, + 0.01907503418624401, -0.018062062561511993, -0.002470092149451375, + -0.03808772936463356, 0.011158271692693233, -0.04685383290052414, + -0.006560939364135265, -0.014025759883224964, 0.0011376449838280678, + -0.005859650671482086, 0.01603611931204796, 0.027334649115800858, + 0.03127744421362877, -0.0018934777472168207, 0.015747811645269394, + 0.01681532710790634, 0.0050960262306034565, -0.01601274311542511, + -0.005072650033980608, -0.014329652301967144, -0.029080074280500412, + 0.006864830385893583, -0.017204932868480682, 0.03066966123878956, + 0.00846220925450325, -0.01464912761002779, -0.04285648837685585, + 0.021599670872092247, -0.023687949404120445, -0.00855571310967207, + -0.07308200001716614, -0.01865426078438759, 0.01531145442277193, + 0.0014181601582095027, 0.009810240007936954, -0.04577852413058281, + 0.02196589857339859, 0.002251913771033287, -0.0351189449429512, + 0.0013480314519256353, 0.02700738050043583, 0.021623047068715096, + -0.012997204437851906, -0.012864738702774048, 0.04727460443973541, + 0.014477700926363468, -0.012927074916660786, 0.00814273301512003, + -0.005610303953289986, 0.00518173910677433, 0.011072558350861073, + -0.005750561598688364, -0.009693358093500137, -0.009428427554666996, + -0.003638905705884099, 0.0159036535769701, -0.005485630594193935, + -0.003701242385432124, -0.019558142870664597, -0.004316817037761211, + 0.022994454950094223, 0.03232158347964287, 0.04605124890804291, + -0.04007471352815628, 0.01918412186205387, -0.036615025252103806, + -0.01489847432821989, 0.05593161657452583, -0.018155567348003387, + -0.03229821100831032, 0.02337626740336418, 0.005649264436215162, + 0.0005610304069705307, -0.041601963341236115, 0.05302516743540764, + 0.027381401509046555, 0.017127012833952904, 0.0003506439970806241, + -0.010527112521231174, 0.020703580230474472, 0.13976670801639557, + -0.013464730232954025, 0.0022129532881081104, -0.002766191493719816, + -0.03004629537463188, -0.0017610121285542846, -0.014049137011170387, + 0.00998945813626051, -0.019215291365981102, 0.04412660002708435, + -0.02331393025815487, 0.014158225618302822, 0.007277811411768198, + -0.012763441540300846, -0.01570885069668293, -0.05846404284238815, + 0.0004207728197798133, -0.011968648061156273, 0.011337489821016788, + -0.03463583439588547, -0.06321722269058228, 0.017376359552145004, + 0.01678415946662426, -0.08861164003610611, -0.00014804970123805106, + 0.005111610516905785, 0.014345236122608185, 0.015093276277184486, + 0.011267360299825668, -0.005158362910151482, 0.002189577091485262, + 0.03795526549220085, 0.0333501435816288, 0.0015194573206827044, + 0.05777055025100708, -0.005859650671482086, -0.02086721360683441, + 0.041407160460948944, 0.027981389313936234, 0.011392033658921719, + -0.011734886094927788, 0.009997250512242317, -0.0025713893119245768, + 0.0315345823764801, -0.006451849360018969, -0.03194756433367729, + -0.025815190747380257, -0.02221524529159069, -0.010262181051075459, + -0.021093184128403664, -0.00021817849483340979, 0.023103544488549232, + -0.021311363205313683, -0.014127057045698166, -0.03778383880853653, + 0.035095568746328354, -0.002080487785860896, -0.09577256441116333, + -0.01047256775200367, 0.018724389374256134, 0.024069763720035553, + -0.00024934683460742235, -0.006475226022303104, -0.0117972232401371, + -0.019885409623384476, 0.02056332305073738, -0.09992574900388718, + -0.013223174959421158, -0.0048233033157885075, 0.0032882613595575094, + -0.006498602218925953, 0.029781363904476166, 0.0034129349514842033, + 0.004301233217120171, 0.030957968905568123, -0.02788788452744484, + 0.003864876227453351, -0.049043409526348114, 0.020298391580581665, + 0.02144382894039154, -0.02499702200293541, -0.006895998492836952, + 0.011142686940729618, -0.022838613018393517, 0.016682861372828484, + -0.0020259430166333914, -0.020711371675133705, 0.07982216030359268, + -0.059983499348163605, 0.0027350231539458036, 0.018584132194519043, + 0.04217857867479324, 0.014945225790143013, -0.04072924703359604, + 0.025784023106098175, 0.004909015726298094, 0.012779026292264462, + -0.02458404004573822, 0.006584315560758114, 0.0033739744685590267, + -0.006740157026797533, 0.003498647827655077, 0.016745198518037796, + 0.005431086290627718, 0.0233684740960598, -0.01979190669953823, + 0.004199936054646969, -0.0400669202208519, 0.003405143041163683, + -0.02759178727865219, 0.017454279586672783, -0.013456937856972218, + -0.015132236294448376, -0.021342530846595764, 0.027404775843024254, + -0.04580968990921974, -0.016667278483510017, -0.026446349918842316, + -0.005220699589699507, 0.003296053735539317, -0.008384287357330322, + -0.047781091183423996, -0.01192189659923315, -0.03818902745842934, + 0.02091396600008011, 0.001558417803607881, 0.0072466423735022545, + 0.010301141999661922, -0.02191135287284851, 0.0022207454312592745, + 0.005220699589699507, 0.007644039113074541, 0.08062474429607391, + -0.016916625201702118, 0.002470092149451375, -0.01870880462229252, + -0.016971170902252197, -0.02560480497777462, -0.014602375216782093, + 0.0215295422822237, -0.005625888239592314, 0.01019984483718872, + 0.006327176000922918, 0.009498557075858116, -0.021326947957277298, + 0.01393225509673357, 0.005353164859116077, 0.007036256603896618, + 0.005555759649723768, 0.03422285243868828, 0.01172709371894598, + 0.05642251297831535, -0.026228170841932297, -0.1293564736843109, + 0.006514186505228281, -0.007121969014406204, 0.023243801668286324, + 0.028394371271133423, 0.014197185635566711, 0.009646606631577015, + -0.012038777582347393, 0.014656919054687023, 0.020321767777204514, + -0.014960811473429203, 0.004262272734194994, 0.051965437829494476, + 0.014298483729362488, -0.0017298436723649502, 0.0006779117393307388, + -0.005119402427226305, -0.02624375745654106, -0.011937480419874191, + -0.014797177165746689, -0.0035921530798077583, 0.039622772485017776, + 0.05270569026470184, 0.01697896234691143, 0.01007517147809267, + -0.0711028128862381 + ] + }, + "background": { + "prompt": "Photo of people with an absolutely stunning or interesting background", + "vector": [ + -0.014705854468047619, 0.001741824671626091, 0.005927403923124075, + 0.010000327602028847, -0.0043155658058822155, -0.010771583765745163, + 0.02611004002392292, -0.00355297583155334, 0.030156968161463737, + 0.03655232489109039, 0.005528777372092009, 0.025919392704963684, + 0.02458486147224903, 0.0015945063205435872, 0.0063086990267038345, + 0.5186651945114136, -0.012028124183416367, -0.009879006072878838, + -0.0039082737639546394, -0.02218443527817726, 0.004506213590502739, + -0.02672531269490719, 0.0031110206618905067, -0.01181147899478674, + 0.005953401327133179, 0.01045094896107912, -0.007365925703197718, + 0.006889307405799627, 0.04576406255364418, 0.03521779179573059, + -0.025555429980158806, 0.0023137673269957304, 0.018926095217466354, + 0.006438686046749353, -0.0043068998493254185, 0.0019931329879909754, + -0.020711250603199005, 0.014021256938576698, 0.0008059189422056079, + -0.021716482937335968, -0.00024264225794468075, -0.009047090075910091, + 0.021248528733849525, -0.03074624016880989, -0.003864944912493229, + -0.020468607544898987, 0.018232833594083786, -0.01973201520740986, + 0.021499838680028915, 0.009463047608733177, 0.012669392861425877, + 0.033042676746845245, 0.02818116545677185, 0.005927403923124075, + -0.018068183213472366, -0.02570274844765663, 0.07050491124391556, + -0.010112982243299484, -0.02271305024623871, 0.008076520636677742, + 0.05238473042845726, -0.010745585896074772, -0.03836347535252571, + 0.014974493533372879, -0.029784338548779488, 0.0220631156116724, + 0.0046448661014437675, -0.035989049822092056, 0.0028250492177903652, + 0.06036592647433281, -0.00710595166310668, 0.026664651930332184, + -0.026629988104104996, 0.023718280717730522, -0.024810170754790306, + 0.011768150143325329, -0.08320896327495575, 0.0017331590643152595, + 0.10377290099859238, 0.04410889744758606, -0.01850147359073162, + -0.07739421725273132, 0.008449150249361992, 0.0003206344263162464, + 0.029567692428827286, 0.024463539943099022, -0.003795618424192071, + 0.00887377467006445, -0.03964601084589958, -0.009168410673737526, + -0.018683454021811485, 0.018024854362010956, -0.01592773012816906, + -0.00891710352152586, -0.00811118446290493, 0.01965402439236641, + 0.026803303509950638, 0.007989862933754921, 0.017738882452249527, + -0.01795552857220173, 0.009437051601707935, -0.03222808986902237, + -0.024402879178524017, 0.06099853292107582, 0.035859059542417526, + 0.03632701188325882, -0.05632766708731651, -0.006845978554338217, + -0.009428384713828564, 0.016369687393307686, 0.0005632766988128424, + -0.015329791232943535, 0.10203107446432114, -0.046647973358631134, + -0.009272401221096516, 0.02673397958278656, -0.01079758070409298, + 0.013042021542787552, -0.006118051242083311, 0.027340583503246307, + 0.000372629176126793, -0.031101539731025696, 0.04186445474624634, + 0.012964029796421528, 0.021673154085874557, 0.01696762628853321, + 0.09339127689599991, -0.022669721394777298, -0.003986265975981951, + -0.012808045372366905, -0.016291694715619087, 0.021621158346533775, + -0.017314258962869644, 0.03532177954912186, 0.026335351169109344, + 0.004714192356914282, -0.02821582928299904, 0.02479284070432186, + 0.002209777943789959, -0.0157284177839756, 0.01334532443434, + -0.017392251640558243, -0.004956834949553013, 0.004835513886064291, + 0.013267332687973976, 0.0009359058458358049, -0.012868705205619335, + -0.010650262236595154, -0.030269624665379524, -0.03364061564207077, + 0.015269131399691105, -0.007556573487818241, -0.06311298906803131, + -0.013631295412778854, -0.04714192450046539, 0.012028124183416367, + -0.006499346345663071, -0.006663996260613203, 0.023865601047873497, + -0.00870912428945303, -0.05077289417386055, 0.025720080360770226, + 0.007816547527909279, 0.012392086908221245, -0.025538098067045212, + 0.01365729235112667, 0.004142250400036573, -0.5228767395019531, + -0.021447842940688133, -0.0018544801278039813, -0.0165516696870327, + 0.07093819975852966, 0.03947269544005394, -0.024229563772678375, + 0.010849575512111187, 0.008041857741773129, -0.0050954874604940414, + -0.016976293176412582, -0.0007019293843768537, 0.05097220838069916, + -0.004497547633945942, 0.028293821960687637, -0.01125686801970005, + -0.01136085670441389, 0.005468116607517004, -0.003431654768064618, + -0.019350720569491386, -0.021612493321299553, -0.0026084042619913816, + 0.014610530808568, -0.030962886288762093, -0.030148301273584366, + 0.04546942561864853, -0.10783714801073074, -0.003925605211406946, + -0.03533044829964638, 0.021629825234413147, 0.003284336533397436, + -0.01100555993616581, -0.02330232411623001, 0.024844834581017494, + 0.012591400183737278, -0.009965664707124233, -0.004722858313471079, + 0.02870977856218815, -0.013908601365983486, 0.00006932636460987851, + -0.017886200919747353, -0.005277469288557768, -0.016083715483546257, + 0.004956834949553013, 0.027409909293055534, -0.020061315968632698, + -0.003535644384101033, 0.004705526866018772, 0.04721991717815399, + -0.0024437541142106056, -0.03354529291391373, 0.010606933385133743, + -0.015347123146057129, 0.022565731778740883, 0.03150016441941261, + 0.02127452753484249, 0.03058158978819847, -0.021586496382951736, + -0.01185480784624815, 0.018388817086815834, 0.005173479672521353, + -0.0035789732355624437, -0.0014471877366304398, -0.003864944912493229, + 0.022739047184586525, -0.008249836973845959, 0.027461905032396317, + -0.022028449922800064, 0.017825540155172348, -0.020061315968632698, + -0.00854447390884161, -0.007027959916740656, -0.02036461792886257, + -0.034524526447057724, 0.0005632766988128424, -0.09423185139894485, + -0.012756050564348698, -0.07407521456480026, -0.0036482997238636017, + -0.03629234805703163, -0.06251505017280579, -0.04469817131757736, + -0.013189340010285378, -0.005156148225069046, -0.02992299012839794, + 0.0010918901534751058, 0.006984631065279245, -0.01935938559472561, + -0.023137671872973442, -0.008189177140593529, -0.04037394002079964, + -0.03426455333828926, 0.011204873211681843, -0.007617233786731958, + -0.004523545037955046, 0.0020711252000182867, -0.0011958797695115209, + -0.016040386632084846, 0.019541367888450623, -0.007088620215654373, + 0.00893443450331688, -0.018258830532431602, 0.020130641758441925, + 0.028805101290345192, -0.009827012196183205, -0.02884843200445175, + -0.02470618113875389, -0.007989862933754921, 0.017149608582258224, + -0.0020711252000182867, -0.0030936887487769127, -0.028432471677660942, + -0.027990518137812614, -0.03516579791903496, -0.002495748922228813, + -0.002781720133498311, -0.059612005949020386, 0.01388260442763567, + -0.004428221378475428, 0.005797416903078556, 0.006404022686183453, + -0.013024689629673958, -0.013908601365983486, 0.013267332687973976, + -0.00565876392647624, -0.014428549446165562, 0.009073087014257908, + 0.006057390943169594, -0.013995259068906307, -0.04328564926981926, + 0.06733322888612747, 0.012903369031846523, 0.012669392861425877, + 0.006430020090192556, -0.002686396474018693, -0.003934271167963743, + 0.005780085455626249, -0.008674461394548416, 0.002036461839452386, + 0.006741988472640514, 0.013068018481135368, 0.0038736106362193823, + 0.0034143230877816677, 0.0636935904622078, 0.01019964087754488, + -0.028623120859265327, -0.003682962851598859, 0.027115274220705032, + 0.06764519959688187, -0.05486314743757248, -0.017036952078342438, + -0.04303433746099472, 0.003241007449105382, 0.03711559996008873, + 0.006499346345663071, 0.0020451275631785393, 0.03806883841753006, + 0.030390942469239235, 0.018215501680970192, -0.0263180211186409, + 0.05684761330485344, 0.005026161205023527, -0.02772187814116478, + 0.0043155658058822155, 0.00480085052549839, 0.009376389905810356, + 0.09580903500318527, 0.019628025591373444, 0.006586004514247179, + -0.03949002921581268, -0.018146174028515816, -0.02420356497168541, + -0.030217627063393593, -0.005303466692566872, 0.02084990404546261, + 0.002625735942274332, -0.005953401327133179, 0.01721893437206745, + -0.026785971596837044, -0.022123774513602257, -0.021257195621728897, + -0.07881540805101395, -0.013891268521547318, 0.007747221272438765, + -0.014610530808568, -0.00005199477163841948, -0.07542707771062851, + 0.036396339535713196, 0.029706347733736038, -0.007383257150650024, + 0.001325866673141718, -0.021040551364421844, -0.017738882452249527, + 0.003145683789625764, -0.012071453034877777, 0.03554708883166313, + 0.003596305148676038, 0.029117072001099586, 0.006915304809808731, + -0.01483584102243185, -0.011187541298568249, -0.008163179270923138, + 0.008267167955636978, -0.014740517362952232, 0.003466318128630519, + -0.011343525722622871, -0.02442021109163761, 0.054941143840551376, + -0.040573254227638245, 0.01577174663543701, -0.020017987117171288, + -0.061795786023139954, -0.004280902910977602, -0.010520275682210922, + -0.04650065675377846, -0.021031884476542473, 0.0007885873783379793, + 0.022938359528779984, 0.013293329626321793, -0.001325866673141718, + -0.05081622302532196, 0.02681197039783001, -0.03442053869366646, + -0.10379889607429504, -0.03584172949194908, 0.035191792994737625, + 0.0017678221920505166, 0.009922335855662823, -0.008969098329544067, + -0.0127473846077919, -0.06911838054656982, 0.01127419900149107, + -0.12100916355848312, -0.021031884476542473, -0.00002599738581920974, + 0.01791219785809517, -0.0077992151491343975, -0.027990518137812614, + 0.008882440626621246, 0.00002599738581920974, 0.043432965874671936, + -0.013778614811599255, -0.045339442789554596, 0.006447351537644863, + -0.004584205336868763, 0.02495748922228813, -0.0038909418508410454, + 0.0056500984355807304, 0.005494114011526108, -0.012418084777891636, + 0.035191792994737625, -0.012634729035198689, -0.01898675598204136, + 0.09869474172592163, -0.08138915151357651, -0.03569440916180611, + 0.01558110024780035, -0.015147809870541096, 0.0030936887487769127, + -0.014064585790038109, 0.028276490047574043, 0.008631131611764431, + -0.018154840916395187, -0.013206670992076397, -0.0013691956410184503, + -0.02682063728570938, 0.0019498037872835994, -0.01621370203793049, + 0.010095651261508465, 0.009740353561937809, 0.0278865285217762, + -0.01866612210869789, 0.016569001600146294, -0.034082572907209396, + -0.03928204998373985, -0.03595438227057457, 0.04257505014538765, + 0.03925605118274689, -0.007686560042202473, -0.007209941744804382, + 0.04557341709733009, -0.013024689629673958, -0.005364127457141876, + -0.02342364378273487, 0.004688194952905178, -0.018423479050397873, + -0.011846141889691353, -0.03744490072131157, 0.02342364378273487, + -0.0050954874604940414, 0.02503548189997673, 0.02118786983191967, + 0.019350720569491386, 0.006178712006658316, -0.047159258276224136, + 0.006005396135151386, -0.01779954321682453, 0.030962886288762093, + 0.1288430541753769, -0.017946861684322357, 0.011066220700740814, + 0.013475310988724232, -0.0294550359249115, -0.0018804774153977633, + 0.002530412282794714, 0.008041857741773129, -0.027583226561546326, + -0.04444686323404312, 0.009151079691946507, -0.041769132018089294, + -0.011724821291863918, -0.004904840141534805, 0.010320961475372314, + -0.013033355586230755, -0.013293329626321793, 0.04311233386397362, + -0.02163849025964737, 0.08099918812513351, -0.023241661489009857, + -0.18601128458976746, 0.027739210054278374, 0.013232668861746788, + 0.043337639421224594, 0.020009320229291916, -0.03266138210892677, + -0.0010138980578631163, 0.0072446041740477085, 0.011724821291863918, + 0.0009359058458358049, -0.034663181751966476, -0.02565941959619522, + 0.021534500643610954, -0.031352847814559937, 0.017444245517253876, + -0.007071288768202066, 0.009047090075910091, -0.020667921751737595, + -0.015468443743884563, -0.0004766187339555472, -0.017262263223528862, + 0.021993787959218025, 0.012357424013316631, -0.00497416639700532, + -0.022149773314595222, -0.017392251640558243 + ] + }, + "sports": { + "prompt": "Photo of people joyfully playing sports together", + "vector": [ + -0.005494717042893171, 0.014157154597342014, -0.007176160346716642, + -0.06306163221597672, -0.02043253928422928, 0.015883635729551315, + -0.017932895570993423, 0.008880123496055603, -0.03749468922615051, + -0.036038439720869064, -0.010418944992125034, -0.015861116349697113, + 0.010839304886758327, 0.020199840888381004, 0.01848086528480053, + 0.5462063550949097, 0.013301420025527477, 0.017745234072208405, + 0.022181542590260506, -0.061507806181907654, 0.010126193054020405, + -0.0285244882106781, 0.01592867448925972, -0.021588532254099846, + 0.024313373491168022, -0.03855309635400772, -0.03058875910937786, + 0.00425615394487977, -0.030160890892148018, 0.02802155539393425, + 0.0383354127407074, -0.011372262611985207, 0.027969012036919594, + -0.0023945558350533247, 0.012610825709998608, -0.004083505365997553, + 0.009570715948939323, -0.010929382406175137, 0.011529898270964622, + 0.02537928707897663, 0.013316432014107704, -0.01292609702795744, + 0.0003377899993211031, 0.012032830156385899, -0.011297198012471199, + -0.025033991783857346, 0.0018315722700208426, -0.008910148404538631, + -0.006342945154756308, 0.019621845334768295, 0.01180013082921505, + 0.03567812964320183, 0.004196102265268564, 0.008797552436590195, + 0.008519813418388367, -0.010914369486272335, 0.01219797134399414, + -0.01037390623241663, 0.0065831514075398445, 0.02144591137766838, + -0.045661699026823044, -0.0022969720885157585, -0.03618856891989708, + -0.029072457924485207, -0.01758759841322899, 0.010741720907390118, + 0.005885052029043436, 0.0196593776345253, 0.010156218893826008, + 0.026677902787923813, 0.02429835870862007, 0.034169334918260574, + -0.012858538888394833, -0.03154958412051201, -0.019306574016809464, + 0.009345523081719875, -0.058467693626880646, 0.06812848895788193, + 0.048529159277677536, 0.023134861141443253, -0.0144949434325099, + -0.024658668786287308, -0.015253094024956226, 0.02557445503771305, + -0.01504291407763958, 0.05085615813732147, -0.012265530414879322, + 0.02958289533853531, 0.030363567173480988, 0.010148712433874607, + -0.04953502491116524, 0.0010358892614021897, -0.018240658566355705, + -0.0057799615897238255, 0.019937116652727127, 0.04628473520278931, + -0.09602243453264236, -0.0007431379635818303, -0.013699259608983994, + 0.029042433947324753, -0.0016814435366541147, 0.03136942908167839, + 0.014419878832995892, -0.03038608655333519, 0.039776645600795746, + 0.0373820923268795, -0.0472155325114727, 0.043604932725429535, + -0.0056523522362113, 0.0011259665479883552, 0.0028824745677411556, + -0.0006980992620810866, 0.06918689608573914, -0.0010208763414993882, + 0.013166302815079689, -0.07102597504854202, -0.008249581791460514, + -0.015883635729551315, 0.007619041018188, 0.04771095886826515, + 0.07880265265703201, -0.03757726028561592, 0.006523100193589926, + -0.01739243045449257, -0.0012460697907954454, -0.013383990153670311, + 0.04241140931844711, -0.008865110576152802, 0.0324128232896328, + 0.023127354681491852, -0.058385126292705536, 0.008444749750196934, + -0.005592301022261381, -0.009713338688015938, 0.012881058268249035, + -0.007011018693447113, -0.00820454303175211, -0.008925162255764008, + 0.027075743302702904, 0.02810412459075451, 0.021903803572058678, + 0.019404157996177673, -0.009540691040456295, -0.05063847079873085, + 0.012866045348346233, 0.010906863026320934, -0.015906155109405518, + 0.016048777848482132, -0.026189984753727913, -0.0010358892614021897, + 0.004714047070592642, 0.019839532673358917, -0.04477594047784805, + 0.06558380275964737, -0.00998357031494379, 0.02143840305507183, + 0.03393663465976715, -0.007829220965504646, 0.014900291338562965, + -0.021986374631524086, -0.017752740532159805, -0.01670183800160885, + -0.031609635800123215, 0.03666897863149643, -0.026017336174845695, + 0.007341302931308746, 0.017880350351333618, -0.5495617985725403, + -0.010741720907390118, -0.027683766558766365, -0.014374840073287487, + 0.054324135184288025, 0.03813273459672928, 0.028156671673059464, + -0.016273971647024155, 0.012858538888394833, -0.007371328305453062, + 0.03422938287258148, -0.04352236166596413, 0.013203836046159267, + 0.014637566171586514, 0.0077091180719435215, 0.02092796564102173, + -0.0005930090555921197, -0.010366398841142654, -0.012798487208783627, + -0.040970172733068466, -0.038800809532403946, 0.021490950137376785, + 0.004714047070592642, 0.024628642946481705, 0.011927739717066288, + 0.06314420700073242, -0.013368976302444935, -0.02664787508547306, + -0.002281959168612957, 0.0007281251018866897, 0.0177001953125, + -0.03793756663799286, 0.058355093002319336, 0.025011472404003143, + 0.016641788184642792, -0.008737500756978989, 0.00806192122399807, + 0.02567203901708126, 0.018345749005675316, -0.004308698698878288, + -0.020237373188138008, 0.014284763485193253, 0.019899582490324974, + -0.025296716019511223, 0.013871908187866211, -0.015163017436861992, + -0.015478287823498249, -0.011650001630187035, -0.0025521910283714533, + -0.002664787694811821, 0.009916013106703758, 0.009383055381476879, + 0.0029575389344245195, 0.0007731637451797724, 0.05487960949540138, + -0.03497252240777016, 0.0012085374910384417, -0.025131573900580406, + 0.0034754835069179535, 0.019877063110470772, 0.002056765602901578, + -0.0093380156904459, -0.07027532905340195, 0.004045973066240549, + -0.03432697057723999, 0.015313146635890007, -0.04633728042244911, + 0.009345523081719875, -0.07399101555347443, -0.03498753532767296, + -0.002154349349439144, -0.01072670891880989, -0.024463500827550888, + -0.01210038736462593, -0.012460697442293167, -0.0177077017724514, + 0.0077916886657476425, -0.04800371080636978, 0.018630994483828545, + 0.00602016830816865, -0.004000934772193432, -0.028171684592962265, + -0.0006755799986422062, 0.0035730674862861633, -0.06694997847080231, + -0.004083505365997553, -0.01709968037903309, -0.01528311986476183, + -0.01997464708983898, 0.01439735945314169, -0.016446620225906372, + -0.029470300301909447, 0.016101323068141937, 0.0025822166353464127, + 0.003452964359894395, -0.01640908606350422, 0.004473840352147818, + 0.014254736714065075, -0.019726933911442757, 0.0068533835001289845, + -0.019509248435497284, 0.01494533009827137, 0.007836727425456047, + -0.03067133203148842, 0.020522618666291237, -0.0002927513269241899, + -0.019103901460766792, 0.011026966385543346, 0.011815142817795277, + 0.031331900507211685, 0.020605187863111496, -0.009593235328793526, + -0.0004503866657614708, -0.0006380477570928633, 0.0030476164538413286, + 0.02152848243713379, -0.012468203902244568, 0.0038207799661904573, + 0.0017640143632888794, 0.006733280140906572, 0.02990567311644554, + -0.03845551237463951, -0.006800837814807892, -0.013759312219917774, + -0.05141913890838623, -0.03612851724028587, 0.021798713132739067, + 0.011214627884328365, 0.017354898154735565, -0.00008257088484242558, + -0.03302835300564766, 0.043011926114559174, 0.00012010310456389561, + -0.02448602020740509, 0.03386157006025314, 0.013399002142250538, + 0.04048225283622742, 0.028764694929122925, -0.019546780735254288, + 0.03656388819217682, 0.02310483530163765, 0.010749228298664093, + 0.00043537377496249974, 0.012956122867763042, 0.012610825709998608, + -0.027608701959252357, 0.0018315722700208426, 0.008902642875909805, + 0.00983344204723835, -0.038305383175611496, -0.0017715208232402802, + -0.02928263694047928, -0.018360761925578117, 0.013691754080355167, + -0.0030250968411564827, -0.050210606306791306, 0.056485991925001144, + -0.02546936459839344, 0.01750502735376358, -0.0015463274903595448, + 0.06896920502185822, 0.011199614964425564, 0.0015313145704567432, + -0.019524261355400085, -0.01533566601574421, 0.05636589229106903, + 0.08704472333192825, -0.023119846358895302, -0.0022594397887587547, + 0.003993428312242031, 0.0024771266616880894, -0.016897005960345268, + -0.03360635042190552, -0.007191173732280731, -0.021956348791718483, + 0.0373445600271225, 0.02390802465379238, 0.010486502200365067, + -0.03097158670425415, -0.010809279978275299, -0.024088179692626, + -0.04976021870970726, 0.017152225598692894, -0.010741720907390118, + -0.004226128105074167, -0.004976772237569094, -0.08818570524454117, + 0.0011935245711356401, 0.039288729429244995, -0.051389116793870926, + -0.007123615127056837, 0.017377417534589767, -0.018218139186501503, + -0.0009533183765597641, -0.018345749005675316, -0.03401920571923256, + 0.013901934027671814, 0.03311843052506447, 0.0007131121819838881, + 0.015808571130037308, -0.01238563284277916, 0.01092187687754631, + 0.015418236143887043, 0.03311843052506447, -0.039093561470508575, + 0.006673228461295366, -0.0020342464558780193, 0.0029275130946189165, + -0.03431195393204689, 0.029192563146352768, -0.0120553495362401, + -0.06441279500722885, 0.016183892264962196, -0.03513015806674957, + -0.020124776288866997, -0.030558735132217407, -0.004383763298392296, + 0.01282851304858923, -0.01631150208413601, 0.008872617036104202, + -0.033185988664627075, 0.02941024675965309, 0.06830864399671555, + -0.031121715903282166, 0.012265530414879322, 0.013008668087422848, + -0.01798544079065323, 0.0006455541588366032, -0.012212984263896942, + -0.02881723828613758, -0.09747868776321411, 0.09067033976316452, + -0.13611434400081635, 0.0013511599972844124, -0.03847803175449371, + -0.00820454303175211, -0.009112823754549026, 0.03943134844303131, + -0.013016173616051674, 0.01494533009827137, 0.012340594083070755, + 0.01140979491174221, 0.01633402146399021, -0.022046426311135292, + 0.017362404614686966, -0.009127836674451828, -0.012535762041807175, + 0.0019141433294862509, -0.01858595572412014, -0.011019459925591946, + 0.037922557443380356, 0.0324353463947773, -0.006763305980712175, + 0.0619506798684597, -0.023855479434132576, 0.0074914307333528996, + 0.01439735945314169, 0.05958615615963936, 0.019839532673358917, + -0.00004503866512095556, -0.01229555532336235, 0.009593235328793526, + -0.013931960798799992, -0.010809279978275299, -0.02095799148082733, + -0.006402996834367514, -0.027360988780856133, -0.03969407454133034, + 0.039086051285266876, -0.002116817282512784, 0.04214868322014809, + -0.006950967013835907, -0.0016814435366541147, -0.061410218477249146, + 0.030904032289981842, -0.03679658845067024, -0.032773133367300034, + 0.017857830971479416, 0.0015988725936040282, 0.009525677189230919, + 0.007041044998914003, -0.01429977547377348, -0.022241592407226562, + -0.003385406220331788, 0.010201257653534412, 0.010846812278032303, + 0.028967367485165596, -0.02282709628343582, -0.004413789138197899, + -0.04995538666844368, -0.009323003701865673, 0.0046014501713216305, + -0.04801872372627258, -0.015425742603838444, -0.018661020323634148, + -0.021581025794148445, 0.01170254684984684, -0.011777611449360847, + 0.014337308704853058, -0.013263886794447899, 0.007123615127056837, + -0.026272553950548172, 0.01780528575181961, -0.034747328609228134, + -0.019103901460766792, 0.05255261808633804, 0.017264820635318756, + -0.0270532239228487, 0.024230802431702614, 0.02319491095840931, + -0.009855961427092552, -0.008024388924241066, -0.02516160160303116, + 0.008677449077367783, -0.0028224231209605932, 0.028449421748518944, + 0.00840721745043993, 0.08323144912719727, -0.009713338688015938, + -0.04470087215304375, -0.017940400168299675, 0.011529898270964622, + 0.006215335801243782, 0.016641788184642792, 0.0046915276907384396, + 0.008887629956007004, 0.02921508252620697, -0.008437243290245533, + -0.013113757595419884, 0.013301420025527477, 0.010148712433874607, + -0.004211115185171366, 0.009548196569085121, 0.004623969551175833, + 0.0022969720885157585, -0.00997606385499239, 0.017362404614686966, + -0.016138853505253792, -0.017947906628251076, -0.0021768687292933464, + 0.028246749192476273, 0.03518270328640938, -0.0038508058059960604, + 0.0022294139489531517, -0.10991685837507248 + ] + }, + "roadtrip": { + "prompt": "Photo of people on a road trip together", + "vector": [ + -0.004912007600069046, 0.0011731297709047794, 0.0019375563133507967, + 0.02452976442873478, 0.015061472542583942, -0.02907847985625267, + -0.0028987659607082605, 0.01586374267935753, -0.020556261762976646, + -0.016181621700525284, -0.03766882047057152, -0.02740582451224327, + -0.009059589356184006, 0.021070925518870354, -0.002868491690605879, + 0.5225347280502319, -0.007871323265135288, 0.010830637067556381, + 0.017354751005768776, -0.0008022694382816553, 0.032658420503139496, + -0.018527882173657417, -0.0190047025680542, 0.0072355614975094795, + 0.025120114907622337, 0.00825731921941042, -0.0001665087474975735, + 0.018641412258148193, 0.03140203654766083, 0.03611725941300392, + 0.0012261098017916083, 0.00574455177411437, -0.02322797104716301, + -0.0031031176913529634, 0.007129601668566465, -0.007651833817362785, + -0.030705727636814117, -0.002891197334975004, -0.010179739445447922, + -0.01948152296245098, 0.031053880229592323, 0.012442744337022305, + -0.017438005656003952, 0.01353261899203062, -0.043587446212768555, + -0.051943160593509674, -0.04392046853899956, -0.008302731439471245, + 0.011148517020046711, 0.04066597670316696, 0.0070009357295930386, + -0.009914839640259743, 0.012972544878721237, 0.01281360536813736, + -0.007311247289180756, -0.046047236770391464, -0.02214566245675087, + -0.0011201497400179505, -0.009771035984158516, -0.007818342186510563, + -0.0008249751408584416, 0.052904367446899414, -0.030183492228388786, + 0.04566124081611633, -0.08333005756139755, 0.01936042681336403, + -0.02107849344611168, 0.0004238404508214444, 0.03165179863572121, + 0.03735850751399994, -0.012041609734296799, 0.02590724639594555, + -0.020253518596291542, -0.005517494399100542, -0.050595950335264206, + 0.022032134234905243, -0.06305383890867233, 0.0010066210525110364, + 0.11184089630842209, 0.06904814392328262, -0.016052957624197006, + -0.011193929240107536, 0.02701982855796814, 0.01279089879244566, + 0.0417180098593235, 0.0484994575381279, -0.018777644261717796, + 0.0058126687072217464, -0.023439889773726463, 0.026868455111980438, + -0.014274340122938156, -0.013456934131681919, 0.02573316916823387, + -0.025786150246858597, 0.005313142668455839, 0.03297629952430725, + -0.055485256016254425, 0.04085519164800644, 0.015462607145309448, + 0.05586368218064308, -0.034747347235679626, -0.0027246885001659393, + -0.002020810730755329, -0.07925816625356674, -0.011322595179080963, + 0.03522416949272156, -0.06007181107997894, -0.009831584990024567, + -0.012041609734296799, 0.014047283679246902, 0.020450301468372345, + -0.013441797345876694, 0.05773312598466873, -0.02776155062019825, + -0.02318255975842476, -0.03757042810320854, 0.0023386909160763025, + -0.0450330451130867, -0.00011352869478287175, 0.007364227436482906, + 0.0449119508266449, -0.057589318603277206, -0.012828742153942585, + -0.02518066205084324, -0.025543956086039543, -0.04224780946969986, + 0.048923294991254807, -0.026096461340785027, 0.000628192035946995, + -0.0224711112678051, -0.017475849017500877, 0.03426295891404152, + 0.004927145317196846, 0.050686776638031006, 0.030804118141531944, + 0.004283815622329712, -0.024257296696305275, -0.02782209776341915, + -0.005721846129745245, -0.004450324922800064, 0.01995077356696129, + 0.07054673135280609, 0.03744933009147644, -0.0414758138358593, + -0.025665052235126495, -0.038879793137311935, -0.006849563680589199, + 0.00522231962531805, -0.047856125980615616, -0.04316360875964165, + -0.016022682189941406, 0.03517875447869301, 0.0066981930285692215, + 0.0018088903743773699, 0.042686786502599716, 0.029086049646139145, + -0.04600939154624939, 0.020359478890895844, 0.0003557232266757637, + -0.008075674064457417, -0.019640464335680008, -0.020972533151507378, + 0.0026868456043303013, 0.005434240214526653, 0.055190082639455795, + -0.032923322170972824, 0.017248792573809624, -0.5268185138702393, + 0.0002573317033238709, 0.03440675884485245, 0.021328255534172058, + 0.04710683599114418, -0.008976335637271404, 0.008590337820351124, + 0.0345429964363575, -0.020654652267694473, -0.020215675234794617, + 0.01556099858134985, 0.006645212881267071, 0.02641434222459793, + 0.019859952852129936, -0.0069706616923213005, 0.033248770982027054, + -0.023031188175082207, 0.07042562961578369, -0.010823068208992481, + 0.030138082802295685, 0.0007114464533515275, -0.010807931423187256, + -0.007462618872523308, 0.014092694967985153, -0.03826673701405525, + 0.0121475700289011, 0.03548149764537811, 0.014796572737395763, + -0.01946638524532318, 0.017218517139554024, -0.09220042824745178, + -0.012639527209103107, 0.010104053653776646, 0.02154017798602581, + 0.02644461579620838, -0.01799808256328106, -0.02150990255177021, + 0.0053434171713888645, -0.007099327631294727, -0.0258769728243351, + 0.01148153468966484, 0.026391636580228806, -0.006350038107484579, + -0.030713295564055443, -0.018558157607913017, 0.024552471935749054, + -0.017074715346097946, 0.005146633833646774, -0.012510862201452255, + 0.00226300535723567, -0.018671685829758644, 0.02938879281282425, + -0.002633865689858794, -0.0011428555008023977, -0.0028609230648726225, + -0.03088737092912197, 0.04949093982577324, 0.031000901013612747, + 0.004018915817141533, 0.005456945393234491, 0.0007568579749204218, + -0.016227034851908684, -0.04377666488289833, -0.024143768474459648, + -0.034611113369464874, -0.014766298234462738, -0.04628186300396919, + -0.001967830816283822, -0.0005600748700089753, -0.019973481073975563, + 0.027352845296263695, 0.007409639656543732, 0.009483429603278637, + -0.02866221033036709, 0.016022682189941406, -0.017839141190052032, + 0.0030804118141531944, -0.056567560881376266, 0.05664324760437012, + -0.02036704681813717, 0.04857514426112175, -0.03882681205868721, + 0.01077765692025423, -0.042936552315950394, -0.042611103504896164, + -0.03981829434633255, 0.06430265307426453, 0.001150424126535654, + 0.002346259541809559, -0.04794694855809212, -0.06702733784914017, + 0.013419090770184994, -0.00125638407189399, 0.008582768961787224, + -0.03512577712535858, -0.008151359856128693, 0.010368953458964825, + 0.03081168793141842, -0.005948903504759073, 0.01629515178501606, + -0.007091759238392115, 0.014122968539595604, -0.03186371922492981, + -0.03385425731539726, 0.01768776960670948, -0.03588263317942619, + -0.06287219375371933, -0.011875101365149021, -0.015333942137658596, + -0.00028003743500448763, -0.018020788207650185, 0.020806023851037025, + -0.008787120692431927, 0.009876996278762817, 0.014804141595959663, + 0.05313899368047714, -0.04797722399234772, -0.002611159812659025, + -0.0019753992091864347, -0.0010293268132954836, 0.006675486918538809, + -0.025279054418206215, -0.011965923942625523, -0.015470176003873348, + -0.028942245990037918, -0.029517458751797676, 0.007878891192376614, + 0.02733014151453972, 0.01634056307375431, -0.018020788207650185, + -0.10486266762018204, 0.01142855454236269, 0.02041245810687542, + -0.009990525431931019, -0.029070913791656494, 0.02555152401328087, + 0.018807919695973396, 0.020344341173768044, -0.02085900492966175, + 0.004647107794880867, -0.005517494399100542, 0.03657137602567673, + 0.0006963092600926757, 0.03387695923447609, 0.007379364687949419, + -0.01353261899203062, -0.02150990255177021, -0.01656005159020424, + 0.041203346103429794, -0.04540390521287918, 0.020927121862769127, + -0.04470759630203247, -0.05180692300200462, 0.022758718580007553, + 0.06918437778949738, -0.0031182547099888325, -0.006864701863378286, + 0.010800362564623356, -0.020314067602157593, -0.03897061571478844, + 0.0869629755616188, 0.002891197334975004, -0.0037615839391946793, + 0.02598293125629425, -0.013956460170447826, 0.00941531267017126, + 0.051958296447992325, -0.0029063343536108732, 0.0208741407841444, + 0.02943420596420765, -0.034656524658203125, -0.03898575156927109, + 0.006569527089595795, -0.021305551752448082, 0.014440849423408508, + 0.07355902343988419, -0.0035875067114830017, 0.010626285336911678, + -0.03583722561597824, -0.01316175889223814, -0.01621946506202221, + -0.0011958355316892266, 0.008620611391961575, -0.02355341799557209, + 0.01207945216447115, 0.040537308901548386, -0.07472458481788635, + -0.02253166027367115, 0.011171223595738411, -0.048226986080408096, + -0.020450301468372345, 0.002346259541809559, 0.018921447917819023, + -0.0008855237392708659, -0.030539218336343765, -0.012162706814706326, + -0.011125811375677586, 0.007326385006308556, 0.006804152857512236, + 0.026331087574362755, 0.009899700991809368, 0.007553441915661097, + -0.011958355084061623, 0.008310300298035145, 0.004889302421361208, + -0.03930363059043884, 0.02355341799557209, 0.019738854840397835, + -0.022493818774819374, 0.02474168688058853, -0.013267719186842442, + -0.08479836583137512, 0.030357571318745613, -0.01624973863363266, + -0.03583722561597824, 0.008045399561524391, -0.025309329852461815, + 0.034997113049030304, -0.0174228698015213, 0.009286646731197834, + -0.027042534202337265, 0.02798103727400303, -0.043292272835969925, + -0.05818723887205124, 0.007606422062963247, -0.019352857023477554, + 0.021971585229039192, -0.005926197394728661, 0.02698955498635769, + -0.017074715346097946, -0.008151359856128693, 0.07210585474967957, + -0.08330735564231873, 0.03763097524642944, 0.008211908861994743, + -0.029948867857456207, -0.0052980054169893265, 0.023931847885251045, + -0.001922419061884284, 0.03935661166906357, 0.09029315412044525, + -0.03508793190121651, 0.015053904615342617, -0.02816268429160118, + 0.004964988213032484, -0.0021192021667957306, 0.002020810730755329, + 0.02364424243569374, 0.04837078973650932, -0.005161771085113287, + 0.01001323014497757, -0.010959303006529808, 0.02909361943602562, + 0.10385604947805405, -0.07229506969451904, -0.026853321120142937, + 0.0004314090183470398, 0.007167445030063391, -0.046864643692970276, + -0.037547722458839417, 0.0433603897690773, 0.0007492893491871655, + -0.008590337820351124, -0.0056158858351409435, 0.000983915408141911, + 0.01420622318983078, 0.02777668461203575, -0.03675302118062973, + 0.007318815682083368, -0.005721846129745245, 0.022387858480215073, + -0.028185389935970306, 0.00484389066696167, -0.020480575039982796, + 0.011875101365149021, -0.0156291164457798, 0.011345300823450089, + 0.01621946506202221, 0.004480598960071802, 0.013752108439803123, + 0.0014683044282719493, -0.005494788289070129, -0.021116336807608604, + -0.071303591132164, 0.0020662222523242235, -0.02361396700143814, + -0.008726571686565876, 0.0031560976058244705, -0.0024900624994188547, + 0.0008855237392708659, 0.03772936761379242, 0.009089863859117031, + 0.0033982922323048115, 0.04201318323612213, -0.02085900492966175, + 0.002278142375871539, 0.0008552494109608233, 0.035383109003305435, + 0.03050137497484684, 0.020049167796969414, 0.011262046173214912, + -0.03053164854645729, -0.055840980261564255, 0.016317857429385185, + 0.01570480316877365, 0.04692519083619118, 0.024340549483895302, + -0.00008325437374878675, 0.003148528980091214, -0.00414001289755106, + 0.019246896728873253, -0.003254489041864872, -0.004026484210044146, + 0.0005222319741733372, 0.006191097665578127, 0.047871265560388565, + -0.021146610379219055, 0.013055799528956413, -0.02017783187329769, + -0.07355145364999771, -0.019935637712478638, 0.00869629718363285, + -0.0010520325740799308, 0.015886448323726654, 0.04725820943713188, + 0.03541338071227074, 0.005631023086607456, 0.004034052602946758, + -0.010361384600400925, 0.007515599485486746, -0.015825899317860603, + 0.009082295000553131, -0.005911060608923435, -0.009967818856239319, + 0.01814945414662361, -0.010247856378555298, -0.021562881767749786, + -0.0366092175245285, 0.004238404333591461, 0.04177099093794823, + 0.0060851373709738255, 0.04944552853703499, -0.007666971068829298, + 0.022804129868745804, -0.055992353707551956 + ] + } + }, + "clip_memory_types": { + "sunrise": { + "prompt": "Photo of an absolutely stunning sunrise or sunset", + "vector": [ + 0.015730585902929306, 0.01055071223527193, 0.01939469203352928, + -0.019281307235360146, 0.012806463055312634, 0.006988056469708681, + -0.018081819638609886, 0.04163592681288719, 0.0742190033197403, + 0.01209631934762001, -0.002148335101082921, -0.03029748983681202, + -0.03369305282831192, -0.011308596469461918, -0.01473399717360735, + 0.5191869139671326, -0.023709263652563095, -0.020820945501327515, + 0.014399810694158077, -0.007214825134724379, -0.005866148043423891, + -0.00647484278306365, -0.003377659944817424, 0.02588743530213833, + 0.011099730618298054, -0.04701869562268257, 0.031532783061265945, + 0.013230162672698498, 0.009476544335484505, -0.030768930912017822, + -0.000035805580409942195, -0.012388731352984905, -0.01604686863720417, + 0.015098019503057003, -0.03659927472472191, 0.034164492040872574, + 0.004624887835234404, 0.0031270210165530443, -0.04144496098160744, + 0.025583088397979736, 0.041230130940675735, -0.016422826796770096, + 0.004636823199689388, -0.023184115067124367, -0.006450972519814968, + -0.03528043255209923, 0.0026615483220666647, 0.041188355535268784, + -0.007155148778110743, 0.019484205171465874, 0.006892574951052666, + 0.042525097727775574, -0.047812387347221375, 0.034898508340120316, + -0.014883186668157578, 0.01724635623395443, 0.04145689681172371, + 0.0011517462553456426, -0.014799641445279121, 0.013128713704645634, + 0.018666643649339676, -0.0030434743966907263, 0.005376805085688829, + 0.014960765838623047, 0.007292403373867273, 0.028608661144971848, + 0.04778851941227913, -0.008903655223548412, 0.04611162468791008, + 0.023643620312213898, 0.013701601885259151, -0.024174736812710762, + -0.04187462851405144, -0.025075843557715416, 0.019740810617804527, + 0.030040884390473366, 0.0019454365829005837, 0.04633242264389992, + 0.0022915571462363005, -0.012394699268043041, -0.012448407709598541, + -0.025970982387661934, 0.031562622636556625, -0.010216525755822659, + -0.02288573607802391, -0.045198578387498856, 0.015903646126389503, + -0.022080110386013985, 0.010950540192425251, 0.023184115067124367, + 0.030351197347044945, 0.011887453496456146, -0.02117900364100933, + 0.02244413271546364, -0.002118496922776103, -0.027277885004878044, + -0.03205793350934982, -0.00811593234539032, 0.03711845353245735, + 0.006695643533021212, -0.013528542593121529, -0.009279613383114338, + -0.019621457904577255, 0.02236655354499817, 0.05651910975575447, + -0.002482520416378975, -0.047746747732162476, 0.03063167631626129, + -0.014268524944782257, -0.03448674455285072, 0.022312846034765244, + -0.012018740177154541, 0.04589679092168808, -0.01978258416056633, + -0.04169560223817825, -0.03168197348713875, -0.001808181987144053, + -0.017144907265901566, -0.02877575345337391, -0.011195212602615356, + 0.01444755308330059, -0.045335836708545685, -0.002822673413902521, + 0.012991459108889103, 0.029909595847129822, 0.01564704068005085, + 0.03973226249217987, 0.010467165149748325, 0.01981242187321186, + 0.010109109804034233, 0.014184977859258652, -0.0038550677709281445, + -0.02150125242769718, 0.027779165655374527, -0.06068449467420578, + -0.041063033044338226, -0.05965806543827057, 0.01097441092133522, + 0.006158560514450073, -0.02341088280081749, 0.0131645193323493, + 0.016536211594939232, -0.005472286604344845, -0.05319515988230705, + 0.05447222664952278, -0.05522414296865463, -0.015760423615574837, + -0.0232557263225317, -0.03621137887239456, 0.0044100540690124035, + -0.0027212242130190134, -0.02476552687585354, 0.024353764951229095, + -0.03384821116924286, -0.02725401520729065, -0.04609968513250351, + 0.009279613383114338, -0.07156343013048172, -0.034164492040872574, + -0.008682853542268276, 0.006588227115571499, -0.0029181549325585365, + 0.057515699416399, 0.00784739013761282, -0.022121882066130638, + -0.07184987515211105, 0.004069901071488857, -0.5235790610313416, + 0.027546430006623268, 0.005042619537562132, -0.02073740027844906, + 0.04766916483640671, -0.019621457904577255, -0.046302586793899536, + -0.024711819365620613, 0.018756156787276268, -0.0039863549172878265, + -0.023005086928606033, -0.015849938616156578, -0.012663241475820541, + 0.02015257440507412, 0.015885744243860245, 0.050837960094213486, + 0.04939976707100868, 0.010592484846711159, -0.004505535587668419, + -0.0480152890086174, -0.029109938070178032, 0.016178155317902565, + 0.03152681514620781, -0.004929235205054283, -0.015277048572897911, + -0.0026973539497703314, -0.07340145111083984, -0.004702466540038586, + -0.021184969693422318, -0.005663249641656876, -0.0696597620844841, + -0.020940298214554787, -0.028447536751627922, 0.058261651545763016, + -0.06172285974025726, 0.03253534063696861, 0.02317814715206623, + 0.016792817041277885, 0.025672603398561478, 0.0007161116227507591, + 0.013242097571492195, -0.027337562292814255, 0.0019693071953952312, + 0.013838857412338257, 0.02570243924856186, -0.013677733018994331, + 0.01089086476713419, 0.004135544877499342, -0.021877212449908257, + -0.02387038804590702, -0.0032583081629127264, 0.02525487169623375, + -0.020098866894841194, -0.03658733889460564, 0.007525139953941107, + 0.02305879443883896, -0.03377063199877739, 0.005030684173107147, + -0.034039173275232315, -0.002482520416378975, 0.004923267289996147, + 0.056495241820812225, -0.016333313658833504, 0.00896929856389761, + 0.0319385789334774, -0.030315391719341278, -0.02822076715528965, + -0.02798803150653839, 0.0642889216542244, 0.017359739169478416, + 0.030237814411520958, 0.04572969675064087, 0.015551558695733547, + 0.0075370753183960915, 0.015581395477056503, -0.04787803068757057, + 0.024819236248731613, -0.07242872565984726, 0.017019586637616158, + -0.05419175326824188, -0.028584789484739304, -0.0478004552423954, + -0.025833727791905403, 0.027904484421014786, -0.02554728277027607, + -0.0031270210165530443, 0.06221219524741173, -0.03958307206630707, + 0.02147141471505165, 0.012155994772911072, -0.026895960792899132, + -0.012388731352984905, 0.009267677552998066, 0.0232557263225317, + 0.01873825490474701, -0.024150865152478218, -0.012490181252360344, + 0.013128713704645634, 0.04901784285902977, -0.008772367611527443, + 0.03884309157729149, -0.0010204591089859605, 0.04531793296337128, + 0.02846544049680233, -0.02359587885439396, 0.00020289829990360886, + -0.010616355575621128, 0.004069901071488857, 0.060063865035772324, + 0.011982935480773449, -0.014059659093618393, -0.008676886558532715, + -0.01345096342265606, -0.01842794008553028, -0.023607812821865082, + 0.027993997558951378, -0.0009488479699939489, -0.006242106202989817, + 0.017532799392938614, -0.03568026423454285, 0.052837107330560684, + 0.0027391270268708467, 0.012931782752275467, -0.028196895495057106, + -0.0020707561634480953, 0.013940306380391121, 0.023106535896658897, + 0.007017894182354212, 0.04989507794380188, -0.032821785658597946, + 0.08213800936937332, 0.002106561791151762, 0.0013606121065095067, + -0.033329032361507416, -0.014757867902517319, -0.026525970548391342, + -0.015945419669151306, -0.019203728064894676, 0.020928362384438515, + 0.022927507758140564, 0.035328175872564316, -0.04256686940789223, + 0.009589928202331066, 0.1263817697763443, -0.014907057397067547, + 0.007602719124406576, 0.00035805581137537956, 0.022491874173283577, + 0.03591896593570709, -0.03532220795750618, 0.015599298290908337, + -0.07282259315252304, 0.00899913627654314, 0.015396400354802608, + -0.03627702593803406, 0.010562647134065628, 0.026794511824846268, + -0.04384990409016609, 0.015306886285543442, 0.020647887140512466, + 0.07979274541139603, -0.015378497540950775, 0.06526760756969452, + -0.015736553817987442, -0.007471431512385607, -0.04404086619615555, + 0.06075610592961311, -0.021214807406067848, 0.04585501551628113, + -0.017174743115901947, -0.0015873807715252042, 0.056328147649765015, + 0.017234420403838158, -0.017634248360991478, 0.022098012268543243, + 0.027248049154877663, -0.027242079377174377, 0.005257453303784132, + 0.008921558037400246, 0.022038336843252182, 0.013176454231142998, + 0.004380216356366873, 0.03504173085093498, -0.016643628478050232, + 0.0297246016561985, 0.011791972443461418, -0.001861890428699553, + 0.016166219487786293, -0.03747054189443588, 0.057521671056747437, + -0.0038431326393038034, -0.013594185933470726, 0.02111932635307312, + -0.0290801003575325, 0.011768100783228874, 0.01912018097937107, + -0.01493092905730009, 0.014662385918200016, 0.039523396641016006, + 0.02468794956803322, 0.0033120163716375828, 0.01601702906191349, + -0.010031530633568764, -0.015020442195236683, 0.013051135465502739, + -0.01444755308330059, -0.023816680535674095, 0.02502213604748249, + 0.012454375624656677, 0.025863565504550934, -0.006773222703486681, + -0.06847818195819855, -0.03054216131567955, 0.001545607578009367, + -0.03384821116924286, -0.0265438724309206, -0.003395562758669257, + 0.0016351215308532119, 0.018421972170472145, 0.005895986221730709, + -0.03442706912755966, -0.003222502302378416, 0.014190945774316788, + 0.04217897355556488, -0.05589848384261131, 0.07648072391748428, + -0.03790617734193802, -0.018111657351255417, -0.03603235259652138, + 0.004421989433467388, -0.0255771204829216, 0.02341088280081749, + -0.028089478611946106, 0.002977831056341529, -0.006874672137200832, + -0.021835438907146454, -0.02285589650273323, -0.007948839105665684, + 0.024371666833758354, -0.05104682594537735, 0.004875526763498783, + -0.033102262765169144, -0.012639370746910572, -0.07154552638530731, + -0.025404062122106552, 0.02471778728067875, -0.035035762935876846, + 0.02645435929298401, 0.023452656343579292, 0.04376635700464249, + 0.010747642256319523, -0.021053681150078773, -0.01824294403195381, + 0.031085213646292686, -0.03658733889460564, -0.006558389402925968, + 0.012239541858434677, 0.030560065060853958, -0.00466069346293807, + -0.03072715923190117, 0.019818389788269997, 0.0128422686830163, + 0.009774924255907536, -0.0230408925563097, 0.04861801490187645, + -0.03312016278505325, -0.008855913765728474, -0.071014404296875, + 0.019925806671380997, 0.008121899329125881, 0.017240388318896294, + -0.016118479892611504, -0.02033757045865059, -0.008867849595844746, + -0.024276185780763626, -0.038496967405080795, -0.0337885357439518, + 0.03429577872157097, -0.02477746270596981, 0.015945419669151306, + 0.07845599949359894, 0.0008473987691104412, -0.034838832914829254, + 0.012925814837217331, 0.031610362231731415, 0.010437327437102795, + 0.02356604114174843, 0.007173051591962576, -0.02039724588394165, + 0.029366547241806984, 0.07196921855211258, 0.03641427680850029, + 0.010377652011811733, -0.05049780756235123, -0.017210550606250763, + -0.018732286989688873, 0.03511931002140045, 0.008265122771263123, + 0.016804754734039307, -0.05049780756235123, 0.012018740177154541, + -0.009028973989188671, 0.0064629074186086655, -0.02353620156645775, + 0.02248590625822544, 0.01842794008553028, -0.00395054928958416, + 0.0018260846845805645, 0.03070925548672676, -0.0533025786280632, + 0.019424527883529663, 0.0476512610912323, -0.0015635105082765222, + -0.00694031547755003, -0.02583969570696354, 0.010789415799081326, + 0.014358039014041424, 0.02073740027844906, -0.043706681579351425, + -0.1288284808397293, 0.023858454078435898, -0.023721197620034218, + 0.007256597746163607, -0.030583932995796204, 0.011236985214054585, + -0.03825826570391655, -0.00011338433978380635, 0.02393006533384323, + 0.016649596393108368, -0.006325652822852135, 0.0010741675505414605, + -0.02848334237933159, -0.05317725986242294, -0.005633411929011345, + -0.0004117641947232187, -0.016655564308166504, -0.0066240327432751656, + -0.018947120755910873, -0.032887428998947144, -0.009339289739727974, + -0.00281073828227818, -0.0724346935749054, 0.006809028331190348, + 0.028817525133490562, 0.011236985214054585 + ] + }, + "mountains": { + "prompt": "Photo of a beautiful mountain range", + "vector": [ + 0.028191275894641876, 0.017811166122555733, 0.016408821567893028, + -0.01227052602916956, -0.007121717091649771, 0.0009211487486027181, + 0.01934412308037281, 0.01587950438261032, 0.060747694224119186, + 0.03486616536974907, -0.007527296897023916, -0.034934911876916885, + 0.016594424843788147, -0.015742018818855286, 0.016945011913776398, + 0.5609795451164246, -0.02234816737473011, 0.01763930916786194, + 0.022746874019503593, 0.007767895702272654, -0.02878246083855629, + -0.020210279151797295, -0.026953911408782005, 0.0035608585458248854, + 0.009871414862573147, -0.014834619127213955, 0.007898506708443165, + 0.005911849904805422, -0.019674086943268776, 0.06762880831956863, + 0.012765471823513508, 0.006894866470247507, -0.030885977670550346, + 0.023221196606755257, -0.004729480016976595, 0.04720543324947357, + -0.0018697944469749928, -0.016367575153708458, -0.026534583419561386, + -0.014552775770425797, 0.05517268180847168, 0.012256776914000511, + -0.013940966688096523, -0.0313534289598465, -0.017467455938458443, + -0.023915493860840797, -0.009713307023048401, -0.015418929047882557, + 0.03967126086354256, -0.010758192278444767, 0.015714522451162338, + 0.019323501735925674, -0.03575294464826584, 0.01787990890443325, + -0.010187629610300064, -0.022086946293711662, -0.01875981315970421, + 0.014277804642915726, -0.012524873949587345, 0.05602509155869484, + -0.006626770831644535, 0.01602386310696602, 0.005052569787949324, + -0.004777600057423115, 0.011748083867132664, 0.038976967334747314, + -0.006778004579246044, -0.011775580234825611, 0.03699030727148056, + 0.0200384221971035, 0.02092519775032997, 0.05463649332523346, + -0.029153671115636826, -0.008297212421894073, 0.006503034848719835, + 0.018780436366796494, -0.024458561092615128, -0.0017666807398200035, + 0.05913224816322327, 0.04763163626194, 0.014085326343774796, + -0.019818445667624474, 0.04759039357304573, 0.006757382303476334, + -0.0011342503130435944, -0.04072989523410797, 0.006331178825348616, + -0.007616662420332432, -0.027950676158070564, -0.00705984840169549, + 0.04116297513246536, 0.040283069014549255, -0.0158382598310709, + -0.00376708572730422, -0.01117752119898796, 0.02269187942147255, + -0.009974528104066849, 0.03630288317799568, 0.002378488425165415, + 0.009369594044983387, -0.022526897490024567, -0.009837043471634388, + 0.03181400150060654, -0.03449495509266853, 0.03248080238699913, + -0.001739183790050447, -0.07973435521125793, 0.02408735267817974, + 0.028761839494109154, -0.0025847158394753933, 0.002447230974212289, + -0.011748083867132664, 0.011225640773773193, -0.016511933878064156, + 0.006723010912537575, -0.007499800529330969, 0.009665187448263168, + 0.010256372392177582, -0.022224431857466698, 0.01949535682797432, + -0.012545496225357056, -0.03436434641480446, 0.02019652910530567, + 0.012353016994893551, 0.00017185609613079578, 0.006846747361123562, + 0.08900771290063858, -0.05629318580031395, 0.03059038706123829, + 0.015460175462067127, 0.016078857704997063, -0.007726650685071945, + -0.021275784820318222, 0.0033958766143769026, -0.0042620315216481686, + -0.015858881175518036, -0.035587962716817856, 0.010373233817517757, + -0.038317035883665085, -0.005774364806711674, 0.011074407026171684, + -0.005361910443753004, 0.03155965358018875, -0.030576638877391815, + 0.008214721456170082, -0.061325132846832275, 0.016003241762518883, + 0.007149213925004005, -0.03575294464826584, -0.006083706393837929, + -0.03567732870578766, 0.004674485884606838, 0.031195318326354027, + 0.002089770045131445, -0.0007630411419086158, -0.024018609896302223, + 0.0002337243058718741, 0.020258398726582527, -0.014724631793797016, + -0.024836644530296326, 0.0029971706680953503, 0.013466645032167435, + 0.029263656586408615, -0.02874808944761753, -0.046380527317523956, + -0.0888427272439003, -0.01563890650868416, -0.5659977793693542, + 0.007719775661826134, -0.0003299637173768133, -0.021715737879276276, + 0.07681968063116074, -0.004983827006071806, -0.008393452502787113, + -0.0009898912394419312, -0.013535386882722378, -0.009802672080695629, + -0.002502224873751402, -0.003457744838669896, -0.034274980425834656, + 0.003457744838669896, 0.008757786825299263, -0.027648210525512695, + 0.015006475150585175, -0.018711691722273827, 0.01304044108837843, + -0.021667618304491043, -0.003925193566828966, -0.0010723820887506008, + 0.021970083937048912, -0.026108378544449806, -0.014133445918560028, + -0.02007966674864292, -0.08935829997062683, -0.006619896739721298, + -0.03663284704089165, 0.005801862105727196, -0.04136919975280762, + -0.0002337243058718741, 0.03229519724845886, 0.016120102256536484, + -0.01900041103363037, 0.040063098073005676, 0.00628993334248662, + 0.01516458299010992, 0.017556820064783096, 0.015006475150585175, + 0.01216053869575262, 0.006152448244392872, 0.02019652910530567, + 0.011218766681849957, -0.003004044760018587, 0.027263253927230835, + -0.015515169128775597, -0.011363125406205654, 0.0010036396561190486, + 0.016285084187984467, -0.020141534507274628, 0.008297212421894073, + -0.0020760218612849712, 0.03229519724845886, -0.001251112436875701, + 0.01255924440920353, 0.02153700776398182, -0.007980997674167156, + -0.004798222333192825, 0.04441449046134949, -0.012380514293909073, + -0.04238659143447876, -0.12834900617599487, -0.01211929228156805, + 0.022018203511834145, 0.0024884764570742846, -0.04711607098579407, + -0.022169437259435654, -0.04123859107494354, -0.026768306270241737, + -0.02793005295097828, 0.022685006260871887, 0.001230489695444703, + 0.018966039642691612, 0.006475538015365601, -0.04930895194411278, + -0.01402345858514309, -0.016890017315745354, 0.04635303094983101, + -0.02617024816572666, -0.020175905898213387, -0.09306351840496063, + 0.025744043290615082, 0.0014779624762013555, -0.013769110664725304, + -0.031119702383875847, 0.0618063323199749, -0.02091832458972931, + -0.019447235390543938, 0.004681359976530075, -0.037313397973775864, + 0.019055403769016266, 0.009005260653793812, 0.011493736878037453, + 0.0022135067265480757, 0.00628993334248662, 0.029682986438274384, + -0.009142744354903698, 0.0007767896167933941, -0.0029078051447868347, + 0.03637849912047386, 0.005781239364296198, -0.017591191455721855, + 0.005788113921880722, 0.03366317227482796, -0.008194099180400372, + 0.014112822711467743, 0.012263651937246323, 0.07155400514602661, + 0.014820870012044907, 0.015212701633572578, 0.023399926722049713, + -0.039863742887973785, -0.007905380800366402, -0.011919938959181309, + -0.00044682587031275034, -0.008104734122753143, 0.01003639679402113, + -0.00793975219130516, 0.014188440516591072, 0.017268100753426552, + 0.029717355966567993, -0.007334818597882986, -0.018010521307587624, + -0.0061180773191154, -0.00934209767729044, -0.01255924440920353, + -0.014167816378176212, 0.005836233496665955, 0.010778814554214478, + 0.024025484919548035, 0.022300047799944878, -0.0062211910262703896, + 0.004825719632208347, 0.013452896848320961, 0.006468663923442364, + 0.006908615585416555, -0.0013336034025996923, -0.006303681991994381, + -0.001196118537336588, -0.008785284124314785, 0.0030796611681580544, + -0.009314601309597492, 0.12067047506570816, 0.002900931052863598, + 0.005478772800415754, 0.000006874244263599394, 0.03638537600636482, + 0.03177963197231293, -0.05441651493310928, 0.019323501735925674, + -0.05815610662102699, 0.01652568206191063, -0.01169308926910162, + 0.0001099879082175903, -0.005547515116631985, 0.03231582045555115, + -0.048649027943611145, 0.013315411284565926, -0.04281279072165489, + 0.02011403813958168, 0.009280229918658733, 0.006186820100992918, + -0.012724226340651512, 0.021688241511583328, -0.01447028387337923, + 0.03657785430550575, -0.02612212672829628, 0.03006106987595558, + -0.011143149808049202, -0.007589165586978197, 0.008352206088602543, + 0.0020004052203148603, 0.01792115531861782, -0.0023234945256263018, + 0.010118887759745121, -0.005774364806711674, 0.0017666807398200035, + 0.00853781122714281, 0.007974122650921345, 0.0260465107858181, + -0.025668427348136902, -0.00188354286365211, -0.0027565720956772566, + -0.02824626863002777, 0.0030590386595577, -0.02823939360678196, + 0.017164988443255424, -0.008874649181962013, 0.011129401624202728, + 0.049879513680934906, 0.023399926722049713, 0.018347356468439102, + 0.03853701055049896, -0.011012539267539978, -0.01439466793090105, + -0.020712098106741905, -0.025070369243621826, 0.015975743532180786, + 0.02597089484333992, -0.008393452502787113, -0.0273594930768013, + 0.013665997423231602, -0.008056613616645336, 0.0016566927079111338, + -0.01768055558204651, -0.01298544742166996, 0.03688719496130943, + -0.032879509031772614, 0.02878246083855629, 0.023255567997694016, + -0.11043473333120346, 0.015996364876627922, -0.030157307162880898, + 0.015267697162926197, 0.0006599274347536266, -0.00775414751842618, + 0.007486051879823208, -0.010187629610300064, -0.016429442912340164, + -0.022671258077025414, 0.0036433495115488768, 0.022588767111301422, + -0.08365267515182495, 0.016553180292248726, -0.004014558624476194, + 0.0021310157608240843, 0.010118887759745121, -0.014710881747305393, + -0.010703197680413723, -0.008702793158590794, -0.0023166202008724213, + 0.006049334537237883, 0.007369189988821745, 0.004853216465562582, + 0.01109503023326397, 0.004097049590200186, 0.03899071365594864, + 0.015398306772112846, -0.05756492167711258, 0.024266080930829048, + -0.014676511287689209, 0.005547515116631985, -0.03958189859986305, + -0.07155400514602661, -0.00853781122714281, -0.013975338079035282, + 0.011961184442043304, -0.006503034848719835, 0.017206232994794846, + -0.010297617875039577, 0.01186494529247284, 0.02049212157726288, + 0.03522362560033798, 0.0033408827148377895, -0.07305946946144104, + 0.016346951946616173, -0.014772751368582249, -0.022685006260871887, + -0.010029522702097893, 0.014140320010483265, -0.028528112918138504, + 0.04158230498433113, -0.0033958766143769026, 0.021055810153484344, + -0.01737808994948864, 0.024568548426032066, -0.016250712797045708, + 0.0015192078426480293, -0.03339507803320885, 0.026672067120671272, + -0.02617024816572666, -0.008585930801928043, 0.0057124970480799675, + 0.023839877918362617, 0.020835833624005318, 0.02408047765493393, + 0.05790863186120987, 0.01695876009762287, 0.04110110551118851, + -0.03364942595362663, 0.0041520437225699425, -0.008895271457731724, + -0.046909842640161514, -0.025902152061462402, -0.0009142744820564985, + -0.03301699459552765, 0.0013817230938002467, -0.018979787826538086, + 0.018773561343550682, 0.06643269956111908, 0.02223818004131317, + -0.011088155210018158, 0.005485646892338991, -0.009135870262980461, + 0.0015879502752795815, 0.018670447170734406, 0.03186212107539177, + 0.07482614368200302, 0.016044486314058304, 0.031380925327539444, + 0.019275380298495293, 0.003258391749113798, -0.019426614046096802, + 0.04843592271208763, 0.000653053168207407, 0.002935302210971713, + -0.02835625782608986, 0.022018203511834145, -0.012284274213016033, + 0.0032927629072219133, 0.005623131524771452, 0.01645006611943245, + -0.01730247400701046, -0.015027097426354885, 0.009727055206894875, + 0.04178165644407272, -0.003045290242880583, 0.0011686214711517096, + -0.060933299362659454, -0.013129807077348232, 0.01575576700270176, + -0.009424588643014431, -0.02685767039656639, 0.003175900550559163, + -0.0057606166228652, -0.0051900544203817844, -0.01298544742166996, + -0.004502630326896906, -0.05713871866464615, -0.0021378900855779648, + -0.00451637851074338, 0.0018560459138825536, -0.007444805931299925, + -0.011954310350120068, 0.001340477610938251, -0.020251523703336716, + -0.006063083186745644, -0.02438981831073761, 0.010318240150809288, + -0.02720825746655464, 0.0004399516328703612, -0.014387793838977814, + 0.01875981315970421, -0.018704818561673164 + ] + }, + "greenery": { + "prompt": "Photo of lush greenery", + "vector": [ + -0.0024188209790736437, 0.01026985514909029, -0.003033661050722003, + -0.026080025359988213, -0.0058240885846316814, 0.01385079137980938, + -0.00429712375625968, 0.006621353793889284, -0.027606990188360214, + -0.01551288552582264, 0.02874883823096752, -0.009891491383314133, + 0.050639841705560684, 0.016256099566817284, 0.01855330355465412, + 0.5217559337615967, -0.01582368277013302, 0.004567382857203484, + -0.0029998787213116884, -0.03520803526043892, -0.025161145254969597, + -0.03884977847337723, -0.025248978286981583, -0.023296354338526726, + -0.011242789216339588, -0.006371363997459412, 0.015810171142220497, + -0.049180444329977036, -0.019958652555942535, 0.043504998087882996, + -0.018661407753825188, -0.01766820438206196, 0.03996460139751434, + 0.00032431120052933693, 0.03405267745256424, 0.017087146639823914, + 0.01268867589533329, 0.01987757533788681, -0.032836511731147766, + 0.015175062231719494, 0.012208965606987476, -0.013506210409104824, + 0.013877816498279572, -0.0033444594591856003, 0.06924044340848923, + -0.012499494478106499, 0.024073351174592972, -0.01617502234876156, + 0.016803374513983727, 0.04507249966263771, -0.009715823456645012, + 0.030127162113785744, -0.013742687180638313, -0.036971479654312134, + -0.04819399490952492, -0.0032431120052933693, -0.012877857312560081, + -0.0001891815336421132, -0.021620746701955795, 0.01589125022292137, + -0.02408010885119438, -0.022796375676989555, -0.002952583134174347, + -0.0651325061917305, 0.04060646519064903, 0.027938058599829674, + -0.026154346764087677, -0.009945543482899666, 0.001378322602249682, + 0.06817292422056198, 0.0241882111877203, -0.009175305254757404, + -0.0001486426335759461, 0.014904802665114403, 0.022316664457321167, + -0.01551964320242405, -0.11554938554763794, 0.008310474455356598, + -0.06501764059066772, -0.010364445857703686, -0.03290407359600067, + 0.000945907726418227, 0.044180646538734436, 0.02203965000808239, + -0.0035809362307190895, 0.02187073789536953, -0.008040214888751507, + 0.0019053284777328372, -0.0007229437469504774, 0.04820075258612633, + 0.07287543267011642, 0.027829956263303757, 0.01050633192062378, + -0.004222802352160215, -0.02354634366929531, 0.022451795637607574, + -0.11383998394012451, 0.024654408916831017, 0.028411012142896652, + 0.0018242505611851811, -0.00024323341494891793, 0.005445725750178099, + 0.011060363613069057, -0.08840858936309814, 0.009080713614821434, + -0.019992433488368988, -0.0661730021238327, -0.012965692207217216, + -0.00035133716301061213, -0.007580774370580912, -0.042998261749744415, + -0.037802524864673615, 0.02318825200200081, 0.030681191012263298, + -0.0008648298680782318, 0.029519077390432358, -0.012708946131169796, + -0.007553748786449432, -0.000020269450033083558, + 0.0035539104137569666, -0.0003378241672180593, -0.03653906285762787, + 0.009688797406852245, -0.0014256180729717016, -0.000493223313242197, + 0.005526803433895111, 0.03970109671354294, 0.046153537929058075, + 0.025924628600478172, 0.037998463958501816, -0.03274191915988922, + 0.018303314223885536, -0.03343108296394348, 0.0015607477398589253, + 0.06490277498960495, -0.007168628741055727, 0.0011215762933716178, + -0.021816685795783997, 0.011594126001000404, -0.07243625819683075, + 0.005648420192301273, 0.003675527172163129, 0.0022499088663607836, + -0.0335054025053978, -0.011431969702243805, -0.027282679453492165, + 0.033451348543167114, 0.0036417446099221706, 0.012567059136927128, + 0.01567504182457924, 0.013479184359312057, -0.008580734021961689, + 0.024309827014803886, 0.015620989724993706, 0.020796455442905426, + -0.017560100182890892, -0.011479265987873077, 0.0016620948445051908, + 0.014148076064884663, -0.04395768418908119, -0.061193469911813736, + -0.004128211177885532, -0.010053647682070732, -0.0012567059602588415, + 0.04771428927779198, -0.00859424751251936, -0.018499253317713737, + -0.5263165235519409, 0.0033444594591856003, -0.04816021770238876, + -0.004574139136821032, 0.07895626127719879, 0.005851114634424448, + -0.015526398085057735, 0.006472711451351643, 0.0330527164041996, + -0.004364688415080309, -0.0242219939827919, 0.02620839886367321, + 0.07151062041521072, 0.0031350082717835903, -0.018789781257510185, + 0.017310110852122307, -0.014188614673912525, -0.03284326568245888, + -0.01953299343585968, -0.040653761476278305, -0.03721471130847931, + -0.00692539568990469, -0.0022499088663607836, -0.008763158693909645, + -0.027154305949807167, -0.008472629822790623, -0.028451552614569664, + -0.016499333083629608, -0.026823241263628006, 0.023303112015128136, + -0.0004391714173834771, -0.016762835904955864, -0.009182061068713665, + 0.03387025371193886, -0.03335675969719887, 0.0240598376840353, + 0.022296395152807236, 0.011067119427025318, 0.006114617455750704, + 0.020776188001036644, 0.010323906317353249, -0.0014458874939009547, + -0.030167698860168457, 0.03322163224220276, 0.018411418423056602, + -0.011573855765163898, 0.003628231817856431, -0.010026621632277966, + -0.025181414559483528, 0.10141481459140778, -0.01784387417137623, + 0.007533479016274214, -0.025181414559483528, 0.00885774940252304, + -0.0226950291544199, 0.020303232595324516, -0.027255654335021973, + 0.0032160861883312464, -0.024073351174592972, -0.005979487672448158, + -0.023911194875836372, -0.028958288952708244, -0.12885290384292603, + -0.006067322101444006, 0.004905207082629204, -0.028329934924840927, + -0.02904612384736538, 0.00012837319809477776, -0.05159250646829605, + -0.025431403890252113, 0.005729498341679573, -0.009587449952960014, + 0.019505968317389488, 0.0365593321621418, -0.011965733021497726, + -0.04020783305168152, -0.01583719812333584, -0.0029390703421086073, + 0.053862687200307846, -0.02485034614801407, -0.04088348150253296, + -0.1935124397277832, -0.03687012940645218, 0.013898086734116077, + -0.0021350488532334566, -0.008040214888751507, -0.06161237135529518, + -0.0033849983010441065, 0.028910990804433823, 0.004648460540920496, + -0.1203937828540802, -0.01955326274037361, 0.03089739941060543, + -0.00010810373350977898, -0.03483642637729645, -0.00107428093906492, + -0.004526843782514334, 0.004243071656674147, -0.0019931625574827194, + 0.0381808876991272, -0.024809807538986206, 0.01412105094641447, + -0.00003378242035978474, -0.0023917951621115208, -0.00417550653219223, + -0.0034255371429026127, -0.019411375746130943, 0.013404862955212593, + -0.004513331223279238, 0.020404580980539322, 0.003418780630454421, + 0.02103968895971775, -0.017256058752536774, 0.016073673963546753, + -0.07371999323368073, -0.006222721189260483, -0.011040094308555126, + -0.00810778047889471, 0.024316584691405296, 0.009830683469772339, + 0.00512817082926631, 0.062058303505182266, -0.009290164336562157, + -0.04211992025375366, -0.012445442378520966, -0.012540033087134361, + -0.01118198037147522, 0.048356153070926666, 0.010938746854662895, + -0.02669486589729786, 0.024627381935715675, 0.04824129119515419, + -0.007965893484652042, -0.026674598455429077, 0.020343773066997528, + 0.017107415944337845, 0.004371444694697857, -0.013621071353554726, + -0.0003716066130436957, -0.005344378296285868, 0.011175223626196384, + -0.0035539104137569666, 0.006580814719200134, 0.004459279123693705, + -0.01477643009275198, 0.05755172669887543, -0.022093700245022774, + 0.016647975891828537, 0.010546870529651642, -0.027275923639535904, + 0.02605975791811943, 0.021080227568745613, -0.019505968317389488, + -0.035120200365781784, -0.005965975113213062, -0.016492577269673347, + 0.01226301770657301, -0.11921814829111099, 0.0170128270983696, + -0.010060404427349567, -0.04658595100045204, 0.03541748598217964, + -0.040309179574251175, -0.016729053109884262, -0.0359107106924057, + 0.030937936156988144, 0.009384755976498127, -0.01067524403333664, + 0.025627342984080315, -0.014452118426561356, -0.00844560470432043, + 0.050329048186540604, -0.053862687200307846, -0.0006148400134406984, + 0.01387106068432331, 0.00809426698833704, 0.013681878335773945, + 0.03153926506638527, -0.029667718335986137, 0.010094186291098595, + 0.005884896963834763, -0.028275884687900543, -0.006013270001858473, + 0.018107375130057335, 0.0240598376840353, -0.0007905085803940892, + 0.034295909106731415, 0.003891734639182687, 0.02855965495109558, + 0.00927665177732706, 0.02070862241089344, 0.02420172281563282, + -0.06411226838827133, -0.015107497572898865, -0.0016147996066138148, + -0.026593517512083054, 0.01109414640814066, -0.0091888178139925, + 0.02204640582203865, -0.01387106068432331, 0.017303355038166046, + -0.0324040949344635, 0.027302948758006096, 0.04524141550064087, + 0.008803698234260082, -0.0058173323050141335, -0.024005787447094917, + 0.010540113784372807, 0.037829551845788956, 0.03985649719834328, + 0.00512817082926631, -0.02721511386334896, -0.018445199355483055, + -0.01351296715438366, 0.03370809555053711, 0.00960096251219511, + 0.003080956405028701, 0.007202411536127329, -0.004655216820538044, + -0.025154387578368187, 0.007864546962082386, 0.0020134320948272943, + 0.011290083639323711, -0.02401929907500744, -0.001033741980791092, + 0.02604624256491661, 0.0003378241672180593, 0.015039931982755661, + -0.01285083219408989, 0.024316584691405296, -0.014073754660785198, + -0.022255856543779373, 0.011195492930710316, -0.016965530812740326, + 0.011918436735868454, -0.007188898511230946, -0.041951004415750504, + -0.03319460153579712, 0.00554707320407033, -0.012661649845540524, + -0.024303071200847626, -0.008790184743702412, -0.022485578432679176, + -0.018066836521029472, -0.0007769956137053668, 0.023654447868466377, + -0.01251976378262043, 0.001331027247942984, 0.016769591718912125, + -0.0048849377781152725, -0.019978921860456467, -0.021262653172016144, + -0.004830885678529739, 0.03161358833312988, 0.029438000172376633, + -0.02486385963857174, 0.031005503609776497, -0.002533681457862258, + -0.007790225557982922, -0.015769632533192635, 0.005743010900914669, + 0.0035403973888605833, -0.001141845714300871, -0.027174577116966248, + 0.017316866666078568, 0.002533681457862258, 0.015303434804081917, + -0.013992678374052048, 0.016810130327939987, -0.04673459380865097, + 0.015154791995882988, -0.028323179110884666, -0.022086946293711662, + -0.027755632996559143, -0.02791779115796089, 0.05092361569404602, + 0.05913950130343437, 0.008830724284052849, -0.02551248110830784, + 0.002256665611639619, -0.0037498483434319496, 0.01850600726902485, + 0.018397903069853783, -0.046849459409713745, -0.018735729157924652, + -0.03820791468024254, 0.002466116566210985, 0.008425334468483925, + -0.029140714555978775, -0.022086946293711662, 0.058990854769945145, + 0.00925638247281313, 0.01327649038285017, -0.0157155804336071, + 0.04408605396747589, -0.0026958370581269264, 0.022749079391360283, + 0.029269086197018623, 0.037322815507650375, 0.03983622416853905, + -0.018641138449311256, 0.011668447405099869, 0.006871343590319157, + -0.008310474455356598, 0.048998016864061356, -0.005040336865931749, + -0.00425658468157053, 0.0035876925103366375, 0.010067160241305828, + -0.014046729542315006, -0.014843993820250034, 0.007655095774680376, + -0.01109414640814066, 0.005324108991771936, -0.0329243466258049, + 0.03744443133473396, -0.010560383088886738, 0.023167982697486877, + -0.01920192502439022, -0.09798252582550049, 0.04036998748779297, + -0.011472509242594242, -0.0017972246278077364, -0.03458644077181816, + -0.019411375746130943, 0.016587166115641594, -0.008067241869866848, + -0.037457942962646484, 0.0022228830493986607, -0.029242059215903282, + -0.044153619557619095, 0.010648217983543873, -0.011398187838494778, + -0.022147752344608307, 0.014594004489481449, 0.004871424287557602, + -0.0006486224010586739, 0.0050065540708601475, -0.01685742661356926, + -0.033640533685684204, -0.006567301694303751, -0.020323501899838448, + 0.026296233758330345, 0.028992071747779846, 0.04254557564854622 + ] + }, + "beach": { + "prompt": "Photo of a beautiful beach", + "vector": [ + -0.014337359927594662, 0.008085873909294605, 0.004238295368850231, + -0.04446237161755562, -0.008224942721426487, 0.03859497979283333, + 0.022509323433041573, 0.008933532051742077, 0.003609173698350787, + 0.029283974319696426, 0.0250787902623415, -0.01219172216951847, + -0.04351537674665451, 0.011549355462193489, -0.0035694397520273924, + 0.5811630487442017, 0.0009138825116679072, 0.005066087935119867, + -0.02306560054421425, 0.028462804853916168, -0.015257864259183407, + -0.03599240258336067, 0.03354213759303093, -0.04589279368519783, + 0.017860442399978638, -0.0337805412709713, 0.03242295980453491, + -0.018317384645342827, 0.015886986628174782, 0.015211507678031921, + -0.011284462176263332, -0.02511190064251423, 0.021277567371726036, + -0.027005890384316444, -0.0029005836695432663, 0.00967523455619812, + -0.007960048504173756, -0.016794247552752495, -0.037469182163476944, + -0.022012649103999138, 0.006648826412856579, 0.018112091347575188, + -0.003728376002982259, -0.03072764351963997, 0.02964157983660698, + 0.014211534522473812, -0.0009072601678781211, -0.003980024252086878, + 0.018098846077919006, -0.00018542543693911284, 0.02858862839639187, + 0.05057479068636894, 0.013999620452523232, 0.010562627576291561, + 0.006728294305503368, -0.02783368155360222, -0.0021125255152583122, + 0.016271082684397697, -0.030118387192487717, 0.030005808919668198, + -0.000715212372597307, -0.02084711566567421, -0.01541017834097147, + 0.0136089026927948, -0.004278029780834913, 0.028621740639209747, + 0.00102646229788661, -0.030999161303043365, 0.01017853245139122, + -0.03440966084599495, 0.04332995042204857, 0.0114698875695467, + -0.004337630700320005, -0.002880716696381569, 0.03437655046582222, + 0.02301924303174019, -0.05721036717295647, 0.006158773321658373, + 0.04152205213904381, 0.026813842356204987, 0.004033003468066454, + -0.035277191549539566, 0.04628351330757141, 0.023575520142912865, + 0.007695155218243599, 0.002331062685698271, -0.008575926534831524, + 0.015383689664304256, -0.016853848472237587, -0.009536165744066238, + 0.027442965656518936, 0.02937006577849388, 0.029072057455778122, + -0.006390554830431938, -0.028582006692886353, 0.010105686262249947, + -0.05311776325106621, 0.010145420208573341, 0.007761379238218069, + -0.001927100121974945, -0.02185371145606041, -0.012906935065984726, + 0.0002119147829944268, 0.006741539109498262, -0.033409688621759415, + -0.03160179406404495, -0.0758257582783699, 0.02498607710003853, + 0.01603267714381218, -0.023515919223427773, -0.024443047121167183, + 0.00582103431224823, 0.008443479426205158, -0.04087306559085846, + 0.006907097529619932, -0.026899931952357292, -0.0013046003878116608, + 0.006681937724351883, -0.04248891398310661, 0.008714995346963406, + -0.02203913778066635, 0.0157479178160429, 0.02273448370397091, + 0.013747971504926682, 0.0025628444273024797, 0.010734807699918747, + 0.062263209372758865, -0.02872769720852375, -0.008344144560396671, + 0.009390474297106266, -0.0065031349658966064, 0.016449885442852974, + 0.009642122313380241, -0.010681829415261745, -0.024760916829109192, + -0.013589034788310528, -0.026330411434173584, 0.004072736948728561, + -0.0035628171171993017, 0.005973347928375006, 0.006417044438421726, + 0.015886986628174782, 0.0069137196987867355, -0.04590604081749916, + 0.006152151618152857, -0.01846969872713089, -0.012642040848731995, + 0.013456588611006737, -0.03586657717823982, 0.00468861497938633, + 0.004417098592966795, -0.00984079297631979, 0.03658178821206093, + -0.040555190294981, -0.009370607323944569, -0.0043641203083097935, + 0.012165232561528683, 0.006079305429011583, 0.021754376590251923, + -0.017310788854956627, -0.02872769720852375, 0.0004238295659888536, + 0.020304085686802864, 0.00770177785307169, -0.03493282571434975, + -0.055217042565345764, 0.019814031198620796, -0.5854675769805908, + 0.01785382069647312, -0.03142298758029938, -0.011244728229939938, + 0.0694948062300682, -0.009244781918823719, 0.008522948250174522, + 0.00046356357051990926, 0.01653597503900528, -0.025747647508978844, + 0.012787732295691967, -0.017727995291352272, 0.014509540051221848, + 0.012721509672701359, 0.01629757136106491, 0.03692615032196045, + 0.010701696388423443, 0.002225105185061693, 0.006364066153764725, + -0.008264676667749882, 0.01925775595009327, -0.009211670607328415, + 0.032257404178380966, 0.027409851551055908, -0.0293568205088377, + 0.05248864367604256, -0.03565466031432152, -0.0049733747728168964, + -0.04603186249732971, -0.00925140455365181, -0.07718995958566666, + -0.003615796100348234, 0.0067746504209935665, 0.023946372792124748, + 0.024979455396533012, 0.04461468383669853, 0.020747780799865723, + 0.013681747950613499, 0.017330655828118324, 0.007840846665203571, + 0.0059137470088899136, 0.02589995786547661, 0.0298601184040308, + 0.025058923289179802, -0.010589116252958775, -0.006079305429011583, + -0.015032704919576645, 0.011099036782979965, 0.005119066219776869, + -0.025025812909007072, -0.011979807168245316, 0.006324331741780043, + -0.0008277921588160098, -0.019184909760951996, -0.00837725680321455, + 0.002331062685698271, -0.0025495998561382294, -0.016734644770622253, + -0.011046057567000389, 0.03548910468816757, 0.00333765777759254, + 0.004721726290881634, -0.11840076744556427, -0.02340996079146862, + 0.052018459886312485, -0.035422880202531815, -0.06260757148265839, + -0.0006357443635351956, 0.015900231897830963, -0.02202589437365532, + 0.005675342865288258, -0.003993269056081772, -0.0024039081763476133, + -0.01565520465373993, 0.0036422854755073786, -0.06417044252157211, + 0.029144903644919395, -0.05456143245100975, 0.0021323924884200096, + 0.014939992688596249, 0.019516026601195335, -0.10220915079116821, + -0.007814357057213783, 0.007979915477335453, -0.021780867129564285, + -0.010748052969574928, 0.03157530352473259, 0.010489782318472862, + -0.01360228005796671, 0.0098341703414917, -0.08183883875608444, + -0.011363930068910122, 0.00719847995787859, 0.04788611829280853, + -0.01499959360808134, 0.002337685087695718, 0.008642150089144707, + 0.006039571017026901, 0.008940154686570168, -0.0023111954797059298, + 0.034244101494550705, -0.0005231646355241537, -0.019244510680437088, + 0.006880608387291431, -0.009019623510539532, -0.014045977033674717, + 0.02710522525012493, -0.005874013062566519, 0.039998915046453476, + 0.031442854553461075, 0.010701696388423443, 0.0038144660647958517, + -0.005827656481415033, 0.016734644770622253, -0.018098846077919006, + -0.015377067029476166, 0.01251621637493372, 0.011516244150698185, + 0.00788058154284954, -0.0015496269334107637, 0.014542651362717152, + 0.028078708797693253, 0.001377446111291647, -0.030767379328608513, + -0.008383878506720066, -0.0006291220197454095, 0.0060130818746984005, + -0.004278029780834913, 0.020853739231824875, -0.025933071970939636, + -0.016277704387903214, 0.02117823250591755, -0.00002648934787430335, + 0.047144416719675064, -0.015939965844154358, 0.0052647581323981285, + 0.010754674673080444, 0.017251187935471535, 0.0027615143917500973, + 0.027284028008580208, -0.011264595203101635, 0.015608848072588444, + 0.017191587015986443, 0.12382446229457855, 0.007834224961698055, + 0.01678762398660183, 0.014423450455069542, 0.052773404866456985, + 0.02674761787056923, -0.04421072080731392, 0.002198615809902549, + -0.037575140595436096, 0.008469969034194946, -0.010211643762886524, + -0.015112172812223434, -0.02072129212319851, -0.01097321230918169, + -0.043634578585624695, 0.009483186528086662, -0.03866782784461975, + -0.0001059573914972134, -0.00037747318856418133, -0.01737038977444172, + -0.007887203246355057, 0.016588954254984856, -0.01361552532762289, + -0.004311141092330217, -0.011873849667608738, 0.016747890040278435, + 0.0027615143917500973, 0.001258244039490819, -0.0365486778318882, + 0.03971415385603905, 0.027118470519781113, 0.009536165744066238, + 0.022727860137820244, -0.004562790039926767, -0.02871445193886757, + -0.018198180943727493, -0.011430153623223305, 0.015165151096880436, + -0.03127729892730713, -0.00659584766253829, -0.00333765777759254, + 0.005330981221050024, -0.004648880567401648, -0.023688098415732384, + 0.00942358560860157, -0.015641959384083748, 0.032118331640958786, + -0.01956900581717491, 0.03055546246469021, 0.0069137196987867355, + 0.017946533858776093, -0.005701832007616758, -0.0027350252494215965, + 0.007748133968561888, -0.004423721227794886, 0.01821804977953434, + 0.004940263461321592, -0.026946287602186203, -0.021277567371726036, + 0.0175094585865736, 0.011297707445919514, -0.003609173698350787, + -0.02441655658185482, -0.015489645302295685, -0.02902570180594921, + -0.05147542431950569, 0.017708130180835724, -0.013231429271399975, + -0.03809168189764023, -0.021376904100179672, -0.022237807512283325, + -0.015834007412195206, -0.00043045193888247013, -0.023217912763357162, + 0.03719104453921318, -0.009774569422006607, -0.009046112187206745, + 0.0021390148904174566, 0.004907151684165001, 0.02403908409178257, + -0.022251052781939507, -0.0006357443635351956, 0.01812533661723137, + 0.005357470363378525, 0.02306560054421425, 0.01927100121974945, + -0.010211643762886524, -0.039939314126968384, 0.01617174781858921, + -0.013112226501107216, -0.024363579228520393, 0.009158692322671413, + 0.0050131091848015785, -0.029634958133101463, 0.013648636639118195, + 0.0024039081763476133, 0.0124764833599329, 0.01138379704207182, + 0.0015628715045750141, -0.008569303900003433, 0.01697305031120777, + -0.03660827875137329, 0.01063547283411026, -0.002880716696381569, + -0.0035760619211941957, 0.014343982562422752, 0.005681965034455061, + -0.0039998916909098625, -0.010132175870239735, -0.005973347928375006, + 0.07218346744775772, -0.028707832098007202, -0.04050883278250694, + 0.010820899158716202, 0.01771475002169609, 0.0035429501440376043, + -0.016555842012166977, 0.0013906907988712192, 0.007092522922903299, + 0.014151934534311295, -0.009880526922643185, 0.02850916050374508, + 0.01268839742988348, 0.018979618325829506, -0.018429962918162346, + -0.012761243619024754, 0.00421180622652173, 0.013238051906228065, + -0.0040992265567183495, -0.0006092549883760512, 0.010463292710483074, + 0.008814330212771893, -0.013502945192158222, 0.01138379704207182, + 0.027277406305074692, 0.007860713638365269, 0.013787705451250076, + -0.022217940539121628, 0.013622147962450981, -0.02139677107334137, + -0.05273367092013359, 0.030330302193760872, -0.006655448582023382, + -0.00335090234875679, -0.03293950483202934, -0.001927100121974945, + -0.03336995840072632, 0.02331724762916565, 0.002284706337377429, + -0.024913230910897255, -0.008145473897457123, -0.029648203402757645, + -0.0158737413585186, 0.03517785295844078, 0.04156840965151787, + 0.06958751380443573, 0.024350333958864212, 0.014291003346443176, + -0.014973104000091553, -0.02298613078892231, -0.003072764491662383, + 0.028734320774674416, 0.034634821116924286, 0.004953507799655199, + 0.0064038001000881195, 0.001430424745194614, -0.04593915119767189, + 0.009648744948208332, 0.022330520674586296, -0.0023045733105391264, + -0.02199278026819229, 0.006549491547048092, 0.020747780799865723, + 0.006132283713668585, 0.022549057379364967, -0.012582440860569477, + -0.10984470695257187, -0.00737066101282835, 0.006854118779301643, + 0.028151554986834526, -0.029807137325406075, 0.007483240682631731, + 0.02718469314277172, 0.01227119006216526, -0.0034237480722367764, + -0.00837725680321455, -0.024476157501339912, 0.006834251806139946, + -0.0037879766896367073, -0.003827710635960102, 0.012456615455448627, + -0.04129689559340477, 0.011277839541435242, -0.00726470397785306, + 0.008728240616619587, -0.017986267805099487, 0.013675126247107983, + 0.022820573300123215, 0.014423450455069542, -0.008238187991082668, + 0.02880716510117054, -0.06673328578472137 + ] + }, + "city": { + "prompt": "Beautiful photo showing a metropolitan city", + "vector": [ + -0.02022508904337883, 0.0024308236315846443, -0.024840475991368294, + -0.01621343567967415, -0.018501268699765205, 0.014394289813935757, + -0.004027541261166334, 0.010938706807792187, 0.018445663154125214, + 0.0036859549582004547, 0.0027088590431958437, -0.01409242209047079, + -0.05214355140924454, 0.004583612084388733, 0.031870801001787186, + 0.5370293259620667, -0.007220976520329714, -0.005624258890748024, + 0.015101294033229351, -0.03704225644469261, -0.0075943381525576115, + -0.006609298288822174, -0.03983850032091141, 0.004742489196360111, + 0.011693374253809452, -0.02916988544166088, 0.017015766352415085, + -0.018954068422317505, -0.041300173848867416, 0.004543892573565245, + -0.0018429774791002274, 0.006267712451517582, 0.03637497127056122, + 0.04597910866141319, -0.03643852472305298, 0.003113996470347047, + 0.007316302508115768, -0.012535424903035164, -0.04145904630422592, + -0.01633259281516075, 0.024808701127767563, -0.03063950128853321, + -0.0009453203529119492, -0.043651554733514786, 0.0011359731433913112, + -0.06291543692350388, -0.010231702588498592, -0.005346223711967468, + 0.016547076404094696, 0.0009691519662737846, 0.04871974512934685, + 0.023418523371219635, -0.0007943868404254317, 0.05904677137732506, + -0.012170006521046162, -0.010048992931842804, -0.009111616760492325, + -0.024419451132416725, 0.0007864429499022663, 0.049530018121004105, + 0.020924149081110954, 0.013679341413080692, 0.021392837166786194, + -0.024562440812587738, -0.07822327315807343, 0.024403564631938934, + 0.023760110139846802, 0.003431751159951091, -0.0020177424885332584, + 0.037240855395793915, 0.027882976457476616, 0.0327446274459362, + -0.044167909771203995, 0.025261500850319862, -0.006069115363061428, + 0.004488285630941391, -0.02721569500863552, 0.0396399050951004, + 0.07103406637907028, 0.08251295983791351, 0.014576999470591545, + -0.003503246232867241, 0.03790019452571869, 0.02272740751504898, + 0.03453994169831276, -0.00006355094956234097, -0.05225476622581482, + -0.008023306727409363, 0.006402757950127125, -0.011296181008219719, + 0.02713625505566597, 0.031886689364910126, -0.0186760351061821, + 0.005568651482462883, 0.0008182184537872672, 0.013655510731041431, + -0.05238186940550804, -0.03210911527276039, -0.015625588595867157, + 0.0030583892948925495, -0.01081160455942154, 0.00936582125723362, + 0.025404492393136024, 0.0222031120210886, 0.011423283256590366, + -0.009977499023079872, -0.055519696325063705, 0.010906931944191456, + 0.026516633108258247, 0.011177022941410542, 0.01776248961687088, + -0.05265196040272713, -0.0033920318819582462, -0.05242953449487686, + 0.014608773402869701, 0.02619093470275402, -0.004813984502106905, + -0.0286058709025383, -0.0017079317476600409, -0.0365656279027462, + -0.02855820581316948, 0.012440097518265247, 0.03320537135004997, + 0.02806568518280983, 0.059038832783699036, 0.06320936232805252, + 0.01823117770254612, -0.026921769604086876, 0.00046074436977505684, + 0.0030425016302615404, -0.016960158944129944, -0.005465381313115358, + 0.0365656279027462, 0.016983991488814354, 0.006982659921050072, + -0.032752569764852524, -0.014719988219439983, -0.0045200614258646965, + -0.03741561993956566, -0.04035485163331032, 0.014457839541137218, + 0.00039719342021271586, 0.0050523001700639725, -0.011661598458886147, + -0.007729384116828442, -0.015403159894049168, 0.04833843931555748, + 0.01579241082072258, -0.0315609872341156, 0.0256110318005085, + 0.02179797552525997, -0.022997498512268066, 0.06011919677257538, + -0.024292349815368652, -0.006188273895531893, -0.058022018522024155, + -0.013401305302977562, 0.0030742769595235586, 0.01505363080650568, + 0.020582562312483788, -0.03634319826960564, -0.002605588873848319, + 0.03014698065817356, -0.020963868126273155, -0.009373764507472515, + -0.02958296611905098, -0.020940037444233894, -0.5408344864845276, + -0.026413362473249435, -0.008031250908970833, -0.04113335162401199, + 0.052993547171354294, 0.007681720890104771, 0.015959231182932854, + 0.03058389574289322, -0.041284285485744476, -0.023926932364702225, + 0.00962796900421381, -0.004385015461593866, 0.004933142103254795, + 0.03788430988788605, -0.015371385030448437, -0.00006355094956234097, + 0.021972740069031715, 0.042150165885686874, 0.024530665948987007, + -0.004964917898178101, -0.022830678150057793, -0.02320403978228569, + 0.017031654715538025, -0.009945723228156567, -0.0012710189912468195, + 0.00988217256963253, -0.06859530508518219, -0.026786724105477333, + -0.06705419719219208, -0.0025499816983938217, -0.0145690543577075, + 0.03293528035283089, 0.018080245703458786, 0.03054417483508587, + 0.0023196095135062933, -0.003821000689640641, 0.010891043581068516, + -0.0018112020334228873, 0.021964795887470245, -0.007729384116828442, + -0.014998022466897964, 0.029980158433318138, -0.063360296189785, + 0.016038671135902405, 0.027970358729362488, -0.047059476375579834, + 0.010001330636441708, 0.008960683830082417, -0.009786846116185188, + -0.06726867705583572, -0.03569180145859718, 0.004861647263169289, + -0.020113874226808548, 0.040672607719898224, 0.010954594239592552, + 0.015093350782990456, -0.020415741950273514, -0.02970212511718273, + 0.010334973223507404, 0.011026089079678059, -0.005536876618862152, + -0.027509616687893867, -0.08001858741044998, -0.0019462477648630738, + 0.06487756967544556, -0.021980684250593185, -0.03148949518799782, + -0.01865220256149769, -0.033689945936203, -0.014497559517621994, + -0.012559256516397, 0.0024546554777771235, -0.031441833823919296, + -0.05378793179988861, 0.0027882978320121765, -0.01620549149811268, + -0.021933021023869514, -0.060548167675733566, -0.0010247590253129601, + -0.051984675228595734, -0.016626516357064247, -0.06469486653804779, + 0.005656034220010042, 0.003034557681530714, -0.03561235964298248, + 0.0037415619008243084, 0.053057096898555756, -0.016904551535844803, + -0.008936851285398006, 0.006672849413007498, -0.09151336550712585, + 0.00577519228681922, 0.017873702570796013, 0.013663453981280327, + 0.016435863450169563, -0.027040928602218628, 0.026270372793078423, + -0.02910633571445942, 0.03319742530584335, -0.02999604493379593, + -0.03689132630825043, 0.021392837166786194, 0.01628493145108223, + 0.04634452611207962, 0.037510946393013, -0.018135851249098778, + -0.0022322270087897778, 0.022965723648667336, 0.04292071983218193, + -0.010620951652526855, 0.018985845148563385, 0.005894350353628397, + -0.018374167382717133, -0.04233287647366524, -0.008523771539330482, + -0.0110816964879632, -0.02910633571445942, 0.03639880567789078, + -0.03484180569648743, -0.004369127564132214, 0.02375216595828533, + 0.0009294326300732791, 0.006045283749699593, -0.012432154268026352, + -0.003963990602642298, 0.022910118103027344, -0.014672324992716312, + 0.003868664149194956, -0.0008817694033496082, -0.0002780354116111994, + 0.0354217104613781, 0.03109230101108551, -0.013369530439376831, + -0.02374422363936901, -0.013981208205223083, 0.04003709554672241, + 0.009810677729547024, -0.001469615614041686, 0.004067260771989822, + 0.001199524151161313, -0.0004130811430513859, -0.00815040897578001, + -0.006100891157984734, 0.058482762426137924, -0.0038051127921789885, + -0.015887737274169922, -0.007848542183637619, -0.0013425137149170041, + -0.0055527640506625175, -0.04345295950770378, 0.03109230101108551, + -0.04988749325275421, 0.003821000689640641, 0.05859397351741791, + 0.025380657985806465, 0.013925601728260517, 0.04548659175634384, + -0.05572623386979103, 0.029511472210288048, -0.0542169027030468, + 0.036287590861320496, 0.01865220256149769, -0.028296060860157013, + 0.01213028747588396, -0.010994314216077328, 0.043262310326099396, + 0.032760512083768845, -0.004837815649807453, 0.04880712926387787, + -0.01974051259458065, -0.014918585307896137, -0.02765260450541973, + 0.05876079201698303, -0.014434007927775383, -0.004281744826585054, + -0.010184039361774921, -0.01478353887796402, 0.011439170688390732, + -0.023005442693829536, -0.005187346134334803, 0.037828702479600906, + -0.019518084824085236, -0.004043429158627987, -0.01927182450890541, + -0.0241334717720747, -0.013957376591861248, -0.004575667902827263, + 0.016562964767217636, -0.03171192482113838, -0.013480745255947113, + -0.01922416314482689, -0.024300292134284973, -0.051079075783491135, + -0.028367552906274796, -0.003407919779419899, 0.015919512137770653, + -0.007348078303039074, -0.03191052004694939, 0.021829750388860703, + 0.00914339255541563, -0.008420499972999096, -0.001835033530369401, + -0.02218722365796566, -0.001850921311415732, 0.02117040939629078, + -0.014505504630506039, -0.001517278840765357, 0.04524827376008034, + 0.01832650601863861, 0.010422355495393276, -0.00021448444749694318, + -0.06717334687709808, 0.034690871834754944, 0.007975644432008266, + -0.01573680341243744, 0.002859792672097683, -0.0015887736808508635, + -0.011184967122972012, 0.026747005060315132, -0.04824311286211014, + -0.053875312209129333, -0.001835033530369401, -0.020383967086672783, + 0.0008023307309485972, -0.014195693656802177, 0.0006513972184620798, + 0.011296181008219719, 0.03794785961508751, 0.023378804326057434, + -0.03809085115790367, -0.04352445527911186, 0.023950763046741486, + -0.034253958612680435, 0.04487491399049759, 0.04095064103603363, + 0.0024784868583083153, -0.016928382217884064, 0.006227992940694094, + -0.019931165501475334, 0.010009273886680603, 0.01927976869046688, + 0.02655635215342045, -0.05090431123971939, 0.02217928133904934, + -0.004369127564132214, 0.027255410328507423, -0.017651276662945747, + 0.029868947342038155, 0.01428307592868805, -0.01884285733103752, + 0.04993515834212303, 0.020042380318045616, -0.001644380739890039, + 0.012575143948197365, -0.038750190287828445, -0.032323598861694336, + 0.031251177191734314, -0.03403947502374649, -0.026349812746047974, + 0.001644380739890039, 0.04008476063609123, -0.010295253247022629, + 0.0118204765021801, 0.0033920318819582462, 0.007578450720757246, + -0.001437840168364346, 0.03484180569648743, -0.0423567034304142, + 0.01086721196770668, -0.016110165044665337, -0.0004686882020905614, + 0.006672849413007498, -0.020431628450751305, -0.03305443376302719, + -0.00284390477463603, -0.03195818141102791, 0.03586656600236893, + 0.024276461452245712, 0.02707270160317421, 0.04344501718878746, + 0.01232888363301754, -0.001549054286442697, -0.0779770165681839, + -0.03746328502893448, -0.015299891121685505, -0.015363441780209541, + -0.008237791247665882, 0.010795717127621174, 0.014028871431946754, + -0.007228919770568609, 0.005187346134334803, 0.0533113032579422, + 0.013155045919120312, 0.00529856001958251, -0.06317758560180664, + -0.012519536539912224, 0.0317198671400547, 0.04698003828525543, + 0.08506294339895248, 0.020074155181646347, 0.0237203910946846, + -0.03108435869216919, 0.01625315472483635, -0.004607443697750568, + 0.05777575448155403, -0.01980406418442726, -0.04331791400909424, + -0.031322672963142395, 0.018866688013076782, -0.012908786535263062, + 0.013552239164710045, 0.002859792672097683, 0.038758132606744766, + -0.0037177305202931166, 0.001294850604608655, 0.0073798540979623795, + 0.022004514932632446, 0.027779707685112953, 0.008086858317255974, + -0.12062764167785645, -0.04426323622465134, 0.021035363897681236, + 0.04329408332705498, -0.025285333395004272, 0.030250251293182373, + -0.0031696034129709005, 0.0241334717720747, -0.02515028789639473, + 0.004528005141764879, -0.06219254434108734, 0.01232888363301754, + -0.008023306727409363, 0.02171853743493557, 0.03409508615732193, + 0.02372833527624607, -0.00884946994483471, -0.011065809056162834, + -0.00729247136041522, -0.04056933522224426, -0.005838742945343256, + 0.001421952387318015, -0.023307310417294502, -0.017389127984642982, + 0.014998022466897964, -0.024014314636588097 + ] + }, + "moon": { + "prompt": "Photo of a beautiful moon", + "vector": [ + 0.02025710977613926, 0.018633542582392693, 0.006263218354433775, + -0.03596823289990425, 0.01744708977639675, -0.014811917208135128, + 0.025377586483955383, 0.027844157069921494, 0.0332893468439579, + 0.03457571193575859, -0.019520258530974388, -0.03809135779738426, + -0.03466938063502312, 0.030654177069664, 0.018358785659074783, + 0.5304065346717834, -0.03515645116567612, 0.039383962750434875, + -0.004383628722280264, -0.007081246003508568, 0.0023042147513478994, + 0.026564037427306175, -0.028431138023734093, -0.023691575974225998, + 0.0031971761491149664, -0.05539482459425926, -0.001954523613676429, + -0.022355254739522934, 0.00783682893961668, 0.0347505584359169, + -0.004302449990063906, -0.02996104024350643, -0.03302083536982536, + 0.014168735593557358, -0.017503291368484497, 0.011108938604593277, + -0.00493314303457737, 0.021318672224879265, -0.002660150406882167, + -0.03516269475221634, 0.03971492499113083, 0.05899789184331894, + -0.04307445511221886, -0.0264141708612442, 0.018964501097798347, + -0.017497045919299126, 0.0006494264234788716, -0.012607615441083908, + 0.019201790913939476, -0.0244658924639225, 0.026663949713110924, + -0.008835946209728718, -0.015923436731100082, -0.011358717456459999, + -0.0240787323564291, 0.01980126090347767, 0.0007743160822428763, + 0.010103576816618443, 0.018927032127976418, 0.055344872176647186, + 0.0006993822753429413, -0.003534378483891487, -0.015892215073108673, + -0.0073934695683419704, -0.021100115031003952, 0.03495662659406662, + 0.037054769694805145, 0.007218623999506235, -0.01709739863872528, + -0.027057351544499397, 0.03955256566405296, 0.05627529323101044, + -0.01544885616749525, -0.010003664530813694, 0.016173215582966805, + 0.0055888136848807335, -0.01970134861767292, 0.030410639941692352, + 0.0495624765753746, 0.02790660224854946, -0.03941518813371658, + 0.027250930666923523, 0.012588880956172943, 0.010559423826634884, + -0.012207968160510063, 0.014861873351037502, 0.0033720219507813454, + 0.03432593494653702, -0.04916282743215561, -0.0025227719452232122, + -0.012726260349154472, 0.07261711359024048, 0.003134731436148286, + 0.033969998359680176, -0.01705993339419365, 0.019058167934417725, + -0.039565056562423706, -0.03470684587955475, -0.003278354648500681, + 0.007356002926826477, -0.011421162635087967, 0.026051988825201988, + -0.03685494884848595, -0.05667494237422943, -0.0015111652901396155, + -0.009273060597479343, -0.05809244140982628, 0.0107717365026474, + 0.020725445821881294, -0.024522092193365097, -0.018358785659074783, + -0.006744043901562691, -0.018227651715278625, -0.029786191880702972, + 0.04217524826526642, 0.0341198667883873, 0.03138478100299835, + 0.03781035542488098, -0.03432593494653702, 0.06708449870347977, + 0.03763550892472267, -0.005395234562456608, 0.01049073413014412, + 0.017034955322742462, 0.025102829560637474, 0.049081649631261826, + 0.0868607833981514, 0.02744450978934765, 0.02859349548816681, + 0.04921278357505798, -0.004989343229681253, 0.028019003570079803, + -0.002722595352679491, 0.02356044016778469, -0.0471833273768425, + -0.0012801194097846746, -0.011015270836651325, -0.02095649018883705, + -0.0052765896543860435, 0.016391772776842117, 0.006344396620988846, + -0.013800310902297497, -0.0002560238935984671, -0.037747908383607864, + -0.034457068890333176, 0.005901038181036711, -0.007918006740510464, + -0.027400799095630646, -0.06263842433691025, -0.03289594501256943, + 0.0012613859726116061, 0.0014362315414473414, -0.0148181626573205, + -0.022605035454034805, 0.012951061129570007, 0.034488290548324585, + 0.009497861377894878, 0.011833298951387405, 0.015105409547686577, + -0.014249914325773716, 0.005120477639138699, -0.03333306312561035, + 0.03558731824159622, -0.005526368971914053, -0.02112509310245514, + -0.06097739562392235, -0.003890313906595111, -0.5348338484764099, + -0.01283866073936224, -0.0047832755371928215, -0.025621119886636734, + 0.05942251905798912, 0.0257397647947073, -0.02598954737186432, + -0.012314124032855034, 0.01257639192044735, -0.026695171371102333, + -0.038309913128614426, 0.020163441076874733, 0.005008076783269644, + 0.016597840934991837, 0.026076968759298325, 0.011596008203923702, + 0.015142875723540783, -0.02873087301850319, -0.013787822797894478, + -0.012145522981882095, -0.033826377242803574, 0.011596008203923702, + 0.04476671293377876, 0.022286565974354744, -0.0026289280503988266, + 0.05146079882979393, -0.07879915088415146, -0.0283187385648489, + -0.03071662038564682, 0.0016610330203548074, -0.025077851489186287, + -0.008298920467495918, -0.007468403782695532, 0.03574967756867409, + -0.017940403893589973, 0.059191472828388214, 0.02376650832593441, + 0.0012176745804026723, 0.04235009849071503, 0.03725459426641464, + -0.027669312432408333, -0.049668632447719574, 0.022505123168230057, + 0.03690490499138832, 0.02529640682041645, -0.0446043536067009, + 0.014786939136683941, -0.015261520631611347, 0.01775307022035122, + 0.022018054500222206, -0.032989609986543655, 0.016010858118534088, + -0.03268988057971001, 0.0076120272278785706, -0.03703603893518448, + 0.0003684246039483696, -0.024853048846125603, 0.020937757566571236, + -0.027544422075152397, -0.025658588856458664, -0.022111721336841583, + -0.017084911465644836, -0.037017304450273514, -0.005195411387830973, + 0.05747423693537712, -0.042481228709220886, -0.011614741757512093, + -0.04056417569518089, -0.01143365167081356, -0.010852914303541183, + 0.03895933926105499, 0.013575509190559387, -0.010721780359745026, + 0.0158859696239233, -0.0040464261546730995, 0.00031846872298046947, + -0.0031971761491149664, -0.005083010531961918, -0.011458629742264748, + -0.018683498725295067, -0.039733655750751495, -0.10297779738903046, + -0.013163373805582523, -0.01376284472644329, -0.04125106707215309, + 0.02078164555132389, 0.10747382044792175, -0.0015611212002113461, + 0.02255507931113243, 0.007786872796714306, -0.025589898228645325, + 0.015536277554929256, 0.019538993015885353, -0.011439896188676357, + 0.006381863262504339, 0.0032596210949122906, 0.014568382874131203, + 0.005876060109585524, -0.011508584953844547, 0.018777165561914444, + -0.008967080153524876, 0.012089322321116924, 0.007849317975342274, + -0.0010365844937041402, 0.008342632092535496, -0.011221338994801044, + -0.033514149487018585, -0.02644539251923561, 0.045822031795978546, + 0.01873345486819744, 0.0034407111816108227, -0.04611552134156227, + -0.027506954967975616, -0.006256973836570978, 0.03975863382220268, + -0.007774383760988712, 0.002535260980948806, 0.014549649320542812, + -0.014768206514418125, 0.047795288264751434, -0.024172401055693626, + -0.014649561606347561, 0.011290028691291809, 0.00993497483432293, + 0.011908232234418392, 0.01636679470539093, 0.004002714529633522, + 0.005164189264178276, 0.0002872463082894683, -0.02679508365690708, + 0.10626864433288574, 0.06183912977576256, 0.03245258703827858, + 0.06080879643559456, -0.007705694064497948, -0.0011427407152950764, + -0.0320279635488987, -0.014268646948039532, -0.011271295137703419, + -0.039839811623096466, -0.054364487528800964, 0.02366659790277481, + 0.006981334183365107, 0.054327018558979034, 0.021874429658055305, + -0.03973989933729172, 0.00262268353253603, 0.011302517727017403, + -0.012982283718883991, -0.054183389991521835, 0.016141993924975395, + -0.0916752815246582, -0.013007261790335178, -0.0034094888251274824, + 0.00017484556883573532, -0.06137079745531082, 0.014193713665008545, + -0.008823457174003124, 0.009791351854801178, 0.005189166869968176, + 0.024478379637002945, 0.016323082149028778, 0.018720965832471848, + -0.04423592984676361, 0.006556709297001362, 0.0031222424004226923, + 0.057836420834064484, 0.0016235660295933485, 0.03446955606341362, + 0.005638769827783108, -0.008929613046348095, 0.019538993015885353, + 0.03247756510972977, 0.048176199197769165, 0.003877825103700161, + 0.049949631094932556, 0.004121359903365374, 0.020269596949219704, + -0.027269665151834488, 0.003521889680996537, 0.003290843451395631, + -0.06808985769748688, 0.0010990293230861425, -0.034525755792856216, + 0.014736983925104141, -0.00008117830293485895, -0.0181339830160141, + 0.018995722755789757, -0.001049073413014412, 0.03494413569569588, + -0.025277674198150635, 0.02605823427438736, 0.01771560311317444, + -0.055544693022966385, -0.006893911398947239, -0.004327428061515093, + -0.02199932001531124, 0.03206542879343033, -0.0031472202390432358, + 0.05089879408478737, -0.003315821522846818, -0.06154564023017883, + -0.020163441076874733, -0.0001998235093196854, -0.023429308086633682, + -0.010084843263030052, -0.0064318194054067135, 0.05909780412912369, + -0.015130387619137764, 0.026332993060350418, -0.008698566816747189, + -0.03749188780784607, 0.022018054500222206, 0.014274892397224903, + 0.042031627148389816, -0.026070723310112953, -0.04804506525397301, + -0.03628045693039894, -0.0060633947141468525, -0.025939591228961945, + -0.0007743160822428763, -0.0039402698166668415, -0.008642367087304592, + 0.05587564781308174, 0.02682630717754364, 0.02716975286602974, + -0.015592479147017002, -0.020200908184051514, 0.003771668765693903, + -0.015117897652089596, 0.0006619153427891433, -0.06335654109716415, + -0.00936672743409872, -0.001161474152468145, -0.015642434358596802, + 0.04652765393257141, -0.03859715908765793, 0.033514149487018585, + -0.039871037006378174, -0.06976962834596634, 0.04631534591317177, + -0.042268916964530945, -0.0018421229906380177, 0.019620170816779137, + -0.047620441764593124, 0.017284734174609184, 0.03624923527240753, + -0.00670657679438591, 0.0035281339660286903, -0.02095649018883705, + 0.011683430522680283, 0.006793999578803778, -0.004820742178708315, + -0.028749607503414154, 0.004777031019330025, -0.04409230500459671, + -0.005563836079090834, 0.0033345548436045647, -0.011240072548389435, + 0.007337269838899374, 0.014905585907399654, 0.022911015897989273, + -0.004008959047496319, -0.005513879936188459, 0.0387907400727272, + -0.03369523957371712, 0.002728839870542288, -0.011427407152950764, + 0.017041198909282684, 0.0006931378156878054, -0.04941260814666748, + -0.014318603090941906, -0.03224651888012886, -0.01539265550673008, + -0.02693246304988861, -0.008623633533716202, -0.027456998825073242, + -0.010409556329250336, 0.005969727877527475, 0.031022600829601288, + 0.009460395202040672, -0.056375205516815186, 0.01032213307917118, + -0.009404193609952927, -0.011558541096746922, -0.03071662038564682, + -0.0077993618324398994, -0.02721346542239189, 0.014968030154705048, + -0.0036904902663081884, 0.013144641183316708, -0.01914559118449688, + -0.03774166852235794, -0.05164188891649246, -0.027456998825073242, + -0.030810287222266197, -0.004283716902136803, 0.004964365623891354, + -0.012451502494513988, -0.021137580275535583, 0.013550532050430775, + -0.025770988315343857, 0.01650417409837246, 0.005757415201514959, + 0.06817728281021118, -0.02665146067738533, 0.008592410944402218, + -0.027007395401597023, 0.049050427973270416, -0.027919093146920204, + 0.002316703787073493, 0.028830785304307938, -0.036124344915151596, + -0.008823457174003124, -0.012239189818501472, 0.015386410057544708, + -0.04643398895859718, 0.02435348927974701, -0.020625533536076546, + -0.11875761300325394, 0.026626484468579292, 0.010403310880064964, + 0.020625533536076546, -0.025521209463477135, -0.01702870987355709, + 0.018052807077765465, -0.009091969579458237, 0.008061629720032215, + 0.018571097403764725, -0.01370664406567812, -0.010059865191578865, + 0.006606664974242449, 0.023860175162553787, 0.006987578235566616, + 0.014043846167623997, 0.002360415179282427, -0.01977003924548626, + -0.03860964998602867, -0.020413219928741455, 0.05174804478883743, + 0.012345346622169018, 0.0019482792122289538, 0.024259822443127632, + -0.005020565818995237, -0.01080920360982418 + ] + }, + "onTheRoad": { + "prompt": "Photo of a nostalgic road trip", + "vector": [ + -0.028973080217838287, 0.012647041119635105, -0.008081350475549698, + -0.004977925214916468, 0.04188457131385803, -0.005273489281535149, + 0.004659026395529509, -0.015703797340393066, -0.002621188759803772, + 0.03169538080692291, -0.007015763316303492, -0.02328735589981079, + 0.0030256451573222876, 0.031788717955350876, -0.004262348171323538, + 0.5301334857940674, 0.015089335851371288, 0.018737221136689186, + 0.020642833784222603, -0.030870914459228516, 0.018169425427913666, + -0.0017811637371778488, -0.04613914340734482, -0.020533941686153412, + 0.011495895683765411, -0.019196122884750366, 0.008594698272645473, + 0.011052549816668034, 0.017586076632142067, 0.03496992215514183, + 0.004534578416496515, 0.011340335942804813, -0.028241947293281555, + -0.0016411596443504095, -0.019297238439321518, 0.007552445400506258, + -0.03332098573446274, -0.017827194184064865, -0.017189396545290947, + -0.0345110185444355, 0.028420841321349144, 0.016100475564599037, + -0.03083980083465576, 0.0034534353762865067, -0.037661112844944, + -0.05464828386902809, -0.028024161234498024, 0.027215249836444855, + -0.023940708488225937, 0.014544875361025333, -0.014015969820320606, + -0.025667425245046616, 0.01144922710955143, 0.03073868528008461, + 0.03635440766811371, -0.03593439608812332, -0.040157854557037354, + 0.009139158762991428, -0.022081764414906502, 0.008921375498175621, + -0.013292615301907063, 0.03142315149307251, -0.01682383008301258, + 0.05030815303325653, -0.07767118513584137, 0.010313638485968113, + -0.03102647140622139, -0.006440190598368645, 0.04801364243030548, + 0.06619862467050552, 0.016644936054944992, 0.04493355005979538, + -0.05138152092695236, -0.0172905120998621, -0.04947590455412865, + 0.0029789770487695932, -0.0670386478304863, 0.022462885826826096, + 0.06652530282735825, 0.05179375410079956, 0.0031112032011151314, + -0.012755932286381721, 0.03672775253653526, 0.009388054721057415, + 0.041814569383859634, 0.011145885102450848, -0.02598632499575615, + 0.021902868524193764, -0.03352321311831474, -0.011729235760867596, + 0.05675612390041351, 0.02020726352930069, 0.018114980310201645, + -0.037062209099531174, -0.0000077780077845091, 0.006541304290294647, + -0.04171345755457878, 0.012118136510252953, 0.015524904243648052, + 0.02827305719256401, -0.01118477527052164, -0.00269119068980217, + 0.019258346408605576, -0.06276852637529373, -0.029486428946256638, + 0.03668108582496643, -0.06087847054004669, -0.021941760554909706, + -0.03342209756374359, 0.037871118634939194, -0.014202643185853958, + -0.002006725873798132, 0.07768674194812775, -0.018791666254401207, + -0.022750671952962875, -0.011425893753767014, 0.01854277029633522, + -0.026235220953822136, -0.041231222450733185, 0.012017021887004375, + 0.03198316693305969, -0.03062201477587223, -0.02393293008208275, + -0.009224717505276203, -0.02770526520907879, -0.025216301903128624, + 0.06107291951775551, 0.003430101554840803, -0.028747517615556717, + -0.013035940937697887, -0.012009243480861187, 0.0052034868858754635, + 0.0017578297993168235, 0.039854515343904495, 0.03901448845863342, + -0.012864825315773487, -0.02987532690167427, -0.027619706466794014, + -0.038158904761075974, -0.017368290573358536, 0.04195457324385643, + 0.08762703835964203, 0.03662663698196411, -0.019266124814748764, + 0.0031189811415970325, -0.04515133425593376, -0.012195915915071964, + 0.010196967981755733, -0.022245101630687714, -0.05293712019920349, + -0.001345595344901085, 0.020075038075447083, 0.02769748494029045, + -0.0164349302649498, 0.027339696884155273, -0.0029400868806988, + -0.03755221888422966, 0.011254777200520039, 0.010725872591137886, + 0.008291356265544891, 0.0013844853965565562, -0.007311326917260885, + -0.0008711368427611887, 0.005172375123947859, 0.05788393318653107, + -0.015532681718468666, 0.03155537694692612, -0.5350958108901978, + 0.005615721456706524, 0.03021756000816822, 0.00598128791898489, + 0.047414734959602356, -0.05283600836992264, 0.041231222450733185, + 0.01253814809024334, -0.02467184141278267, -0.03131425753235817, + 0.019903922453522682, 0.0127870449796319, 0.034487687051296234, + 0.028234168887138367, -0.009108047001063824, 0.024228494614362717, + -0.018698330968618393, 0.04494910687208176, -0.014887107536196709, + 0.02439183183014393, -0.01256148237735033, -0.025340748950839043, + -0.013432620093226433, 0.031399816274642944, -0.05150596797466278, + 0.012499258853495121, 0.06501636654138565, 0.025885211303830147, + -0.03263652324676514, 0.03205316886305809, -0.11994465440511703, + -0.022525111213326454, 0.02953309379518032, 0.02495962753891945, + -0.001703383750282228, -0.02562853693962097, -0.006650196388363838, + -0.010593647137284279, 0.0019678359385579824, -0.006984651554375887, + 0.03988562524318695, 0.019157234579324722, -0.03468991443514824, + -0.03913893550634384, 0.007435775361955166, 0.021739531308412552, + -0.02215954288840294, 0.00023334022262133658, 0.0072335475124418736, + 0.007435775361955166, 0.001571157481521368, 0.01637270487844944, + -0.0014933774946257472, 0.008128018118441105, -0.010632536374032497, + -0.03667330741882324, 0.01813831366598606, 0.034378793090581894, + 0.0035934397019445896, 0.0032200952991843224, 0.0061446260660886765, + 0.006525748409330845, -0.06574749946594238, -0.038151130080223083, + -0.011488117277622223, -0.030147558078169823, -0.08026126027107239, + -0.02649189531803131, -0.04459131881594658, -0.029851993545889854, + 0.016956057399511337, 0.009325831197202206, 0.013689294457435608, + -0.030139779672026634, 0.03228650987148285, -0.0043556843884289265, + 0.023847371339797974, -0.10854987800121307, 0.008586919866502285, + -0.011721457354724407, 0.04104454815387726, -0.06776978075504303, + 0.03004644252359867, -0.03847780451178551, -0.026764124631881714, + -0.04989591985940933, 0.04401574656367302, -0.0460146926343441, + -0.0022245103027671576, -0.0211250688880682, -0.07163545489311218, + 0.012491480447351933, -0.004977925214916468, 0.00010889210534514859, + -0.01981058530509472, -0.05246266350150108, -0.016691604629158974, + 0.03320431336760521, -0.0081824641674757, -0.0058179497718811035, + -0.02873195894062519, 0.014568207785487175, -0.011091439053416252, + -0.04647359624505043, -0.006300186738371849, -0.02443072386085987, + -0.03892115131020546, -0.008859150111675262, 0.026258554309606552, + 0.00787134375423193, -0.028607511892914772, -0.02695857547223568, + -0.007490221876651049, 0.01284926850348711, -0.03438657149672508, + 0.04619358852505684, -0.014474872499704361, 0.02334180288016796, + 0.006665752734988928, -0.029463093727827072, 0.018293874338269234, + -0.034254346042871475, -0.007311326917260885, -0.03447213023900986, + -0.004806808661669493, -0.03904559835791588, -0.023940708488225937, + 0.013728183694183826, 0.020557275041937828, -0.0432768352329731, + -0.05826505646109581, 0.025791874155402184, 0.010578090324997902, + 0.00437901820987463, -0.01825498417019844, 0.034767694771289825, + 0.026826348155736923, 0.022112876176834106, 0.004254570230841637, + 0.014093750156462193, 0.0049701472744345665, 0.01739940233528614, + 0.006463524419814348, 0.06456524133682251, 0.009730287827551365, + 0.005825727712363005, -0.018457211554050446, 0.0001866721868282184, + -0.005887951701879501, -0.03931005299091339, 0.030015332624316216, + -0.017889417707920074, -0.034425463527441025, -0.03558438643813133, + 0.018962783738970757, -0.029890885576605797, 0.013098164461553097, + 0.014871550723910332, -0.032815415412187576, -0.0028934190049767494, + 0.05481162294745445, -0.01762496493756771, 0.0022011762484908104, + 0.03169538080692291, -0.009123602882027626, 0.010998102836310863, + 0.042390141636133194, 0.006160182412713766, 0.02678745985031128, + 0.012281473726034164, -0.016893833875656128, -0.0006844646413810551, + 0.052851561456918716, -0.018986117094755173, 0.020230598747730255, + 0.04578135535120964, 0.008151352405548096, 0.027977492660284042, + -0.024578504264354706, -0.02076728083193302, -0.017531629651784897, + 0.011527007445693016, 0.0008711368427611887, -0.025846319273114204, + 0.004386796150356531, 0.04838698357343674, -0.008423582650721073, + -0.005374603439122438, 0.004410130437463522, -0.014599321410059929, + 0.007692449726164341, -0.00984695740044117, 0.016279369592666626, + -0.03129870444536209, -0.021591750904917717, -0.008485806174576283, + -0.030482012778520584, -0.016341593116521835, 0.026172997429966927, + 0.016932722181081772, -0.024111824110150337, 0.0011200332082808018, + -0.009551393799483776, -0.0358099490404129, 0.010896989144384861, + -0.047080282121896744, -0.007357995491474867, 0.00798801425844431, + 0.000575572601519525, 0.016046030446887016, -0.00489236693829298, + -0.10168967396020889, 0.02358292043209076, 0.0017811637371778488, + -0.02100062184035778, 0.014350424520671368, -0.05210487172007561, + 0.028871964663267136, 0.009131381288170815, -0.015066001564264297, + -0.004589024931192398, 0.039691176265478134, -0.0756644606590271, + 0.04063231125473976, 0.004775696899741888, -0.02403404377400875, + 0.01830943115055561, -0.004013451747596264, 0.01505044475197792, + 0.015066001564264297, -0.02248622104525566, 0.05625055357813835, + -0.010313638485968113, 0.04159678518772125, -0.0014467095024883747, + -0.02043282613158226, -0.008820260874927044, 0.020572829991579056, + 0.015447122976183891, 0.0064324126578867435, 0.06067623943090439, + -0.04028230160474777, 0.02981310337781906, 0.004526800476014614, + 0.0035623274743556976, -0.01305149681866169, -0.010313638485968113, + 0.01038364041596651, 0.030474234372377396, -0.018986117094755173, + 0.005584609694778919, -0.013261503539979458, 0.015509347431361675, + 0.04229680448770523, -0.01998170092701912, -0.028716403990983963, + -0.006556860636919737, -0.04452909529209137, -0.05336491018533707, + -0.03118981048464775, 0.05527830123901367, -0.006113514304161072, + -0.011744791641831398, 0.0033523214515298605, -0.010391417890787125, + 0.018970560282468796, 0.019266124814748764, -0.03332098573446274, + 0.013152611441910267, -0.030808690935373306, -0.015641573816537857, + -0.0258152075111866, -0.007357995491474867, -0.005989065859466791, + 0.02151397056877613, -0.010461420752108097, 0.023668477311730385, + 0.014124861918389797, 0.014319312758743763, 0.020176151767373085, + -0.023334022611379623, 0.01225036196410656, -0.04085009917616844, + -0.01138700358569622, -0.01356484554708004, -0.011239221319556236, + -0.02076728083193302, 0.007101321592926979, -0.030233116820454597, + 0.007505777757614851, 0.03353099152445793, 0.01745384931564331, + 0.00538238137960434, 0.026141883805394173, -0.03178093954920769, + 0.01402374729514122, 0.03107313998043537, 0.03692220151424408, + -0.03624551743268967, -0.008081350475549698, 0.026414114981889725, + -0.03165648877620697, -0.031500931829214096, -0.005716835614293814, + 0.05883285030722618, 0.06266740709543228, 0.00887470692396164, + 0.008913597092032433, -0.0015089336084201932, -0.005623499397188425, + 0.029377536848187447, 0.003305653342977166, 0.023995155468583107, + -0.019787251949310303, -0.001804497791454196, 0.0464969277381897, + -0.009528059512376785, 0.02586965449154377, 0.013572623021900654, + -0.09108047187328339, 0.0117603475227952, -0.003056757152080536, + -0.016092699021100998, -0.0019211678300052881, 0.033056534826755524, + 0.028241947293281555, 0.026188552379608154, -0.020518384873867035, + -0.02048727311193943, -0.0033134312834590673, -0.014474872499704361, + 0.01888500340282917, -0.028514178469777107, 0.0027689707931131124, + 0.00927138514816761, -0.024119602516293526, -0.005856839939951897, + -0.017267178744077682, 0.0059268418699502945, 0.04030563682317734, + -0.015556016005575657, 0.021249517798423767, -0.017407182604074478, + 0.01837943308055401, 0.00439457455649972 + ] + }, + "food": { + "prompt": "Photo of delicious looking food", + "vector": [ + -0.03325869143009186, 0.019341137260198593, -0.013895140029489994, + -0.03983273357152939, -0.03497690334916115, 0.013626201078295708, + 0.01961754634976387, -0.015852412208914757, -0.01781715452671051, + -0.032451875507831573, 0.02271033637225628, 0.03895868360996246, + 0.0020319772884249687, 0.021567348390817642, 0.0059166401624679565, + 0.5388176441192627, -0.009412836283445358, 0.0056551722809672356, + -0.003092789091169834, -0.05278659239411354, 0.026774289086461067, + -0.007037215866148472, 0.01334232185035944, -0.018474560230970383, + -0.022807452827692032, -0.0056551722809672356, -0.03127153590321541, + 0.024988839402794838, 0.009196192026138306, 0.0060585797764360905, + -0.014388193376362324, -0.006036167964339256, 0.041065365076065063, + -0.011175875551998615, -0.030292898416519165, 0.0198715440928936, + 0.006290165241807699, -0.0056925248354673386, 0.016285700723528862, + 0.0009114016429521143, 0.05141201615333557, -0.029986606910824776, + -0.01359631959348917, 0.022740216925740242, -0.039907436817884445, + -0.04615277796983719, -0.021769052371382713, 0.0008964606677182019, + -0.02143287844955921, -0.023248212411999702, 0.038293808698654175, + 0.016278231516480446, 0.01688334159553051, -0.0026819114573299885, + -0.03274322301149368, 0.010608118027448654, -0.04260428994894028, + -0.010877056047320366, -0.005221882835030556, 0.05776941776275635, + -0.016225937753915787, -0.0020618592388927937, -0.0018526853527873755, + -0.025810595601797104, -0.08053204417228699, 0.03278804570436478, + 0.016659226268529892, -0.014141666702926159, 0.058658406138420105, + 0.038801804184913635, 0.04038555175065994, -0.02264310047030449, + 0.006320047192275524, -0.015307065099477768, 0.005162118934094906, + 0.015986880287528038, -0.046683184802532196, 0.005931580904871225, + 0.01092934887856245, 0.0175482165068388, -0.01203498337417841, + -0.02484690025448799, 0.02037953771650791, 0.04440468177199364, + -0.010764997452497482, 0.02353956177830696, -0.003369197715073824, + 0.02812645211815834, -0.003204846754670143, -0.02384585328400135, + 0.016173643991351128, 0.05445251241326332, -0.030449779704213142, + 0.019453195855021477, 0.002510089660063386, -0.013842846266925335, + -0.11064565181732178, 0.017436157912015915, -0.015172596089541912, + -0.010899467393755913, -0.019371019676327705, -0.006260283291339874, + 0.022531043738126755, 0.07672955840826035, 0.009509953670203686, + 0.010451236739754677, -0.0635366439819336, 0.02775292657315731, + 0.007963558658957481, 0.008501434698700905, -0.006006286013871431, + -0.008972076699137688, 0.10791891813278198, -0.06624843925237656, + 0.012453332543373108, -0.021500114351511, 0.00781414844095707, + 0.005924110766500235, -0.05336181819438934, 0.0006275224150158465, + -0.019079670310020447, -0.021888580173254013, 0.01606905646622181, + -0.02204546146094799, -0.013730787672102451, 0.03783810883760452, + 0.06955787539482117, -0.004198423586785793, 0.002838792046532035, + 0.00573734799399972, -0.014881246723234653, 0.009076663292944431, + 0.007784266024827957, 0.01649487391114235, -0.014156606048345566, + -0.02323327027261257, 0.008352024480700493, 0.026975994929671288, + 0.008172732777893543, 0.019796838983893394, -0.009495011530816555, + -0.007067097816616297, -0.0028238508384674788, -0.04082631319761276, + 0.004952944815158844, 0.005550585221499205, 0.0071791550144553185, + 0.022822393104434013, -0.018713615834712982, -0.018907848745584488, + -0.012879150919616222, 0.044188037514686584, 0.054788682609796524, + -0.021507585421204567, 0.007433152757585049, -0.0055729965679347515, + 0.02680417336523533, -0.005162118934094906, 0.020237598568201065, + -0.01447036862373352, 0.015045597217977047, 0.008202614262700081, + 0.03884662687778473, 0.03740482032299042, 0.02749892883002758, + -0.08252666890621185, -0.020626064389944077, -0.5434120297431946, + 0.004355304408818483, 0.022000636905431747, 0.013252676464617252, + 0.06556862592697144, 0.007866442203521729, 0.005595408380031586, + 0.004317952319979668, 0.003795016324147582, -0.008150320500135422, + 0.03689682483673096, 0.001180339721031487, 0.0028238508384674788, + -0.00079187355004251, 0.027416754513978958, 0.0027342047542333603, + -0.002689381828531623, -0.0005154648097231984, 0.033213865011930466, + -0.04651883617043495, -0.02916485257446766, 0.0355297215282917, + -0.0036381359677761793, -0.013118207454681396, -0.06837005913257599, + -0.04045278578996658, -0.01098164264112711, -0.002308386145159602, + -0.041267070919275284, -0.0024577961303293705, 0.030001547187566757, + 0.019341137260198593, 0.025960005819797516, 0.03824898600578308, + -0.003966838121414185, 0.01657705195248127, -0.00017929212481249124, + 0.01197521947324276, 0.023569442331790924, 0.045973487198352814, + -0.019333666190505028, 0.02383091114461422, -0.034999314695596695, + -0.002046918496489525, 0.004452420864254236, 0.005259235389530659, + 0.00938295479863882, -0.015971940010786057, 0.011459754779934883, + -0.07136573642492294, -0.00008217555296141654, 0.003354256972670555, + 0.011960278265178204, -0.006925158202648163, 0.022015579044818878, + -0.00782161857932806, -0.005565526429563761, 0.0022785039618611336, + -0.02211269550025463, 0.027215048670768738, -0.030121076852083206, + -0.002756616333499551, -0.05776941776275635, -0.034961964935064316, + 0.05590179190039635, -0.01955031231045723, -0.051270075142383575, + -0.029695259407162666, 0.004639183636754751, -0.016539698466658592, + -0.011616635136306286, 0.015165125951170921, -0.012124629691243172, + -0.01593458652496338, -0.003697899868711829, -0.024772195145487785, + 0.039907436817884445, 0.019206669181585312, 0.0040639545768499374, + 0.03631412610411644, -0.015419123694300652, -0.03551478311419487, + 0.01601676270365715, -0.00938295479863882, -0.02476472407579422, + -0.014948480762541294, 0.05316758155822754, -0.031839292496442795, + -0.042066413909196854, -0.00469147739931941, 0.006529221311211586, + 0.005535644479095936, -0.0032870222348719835, -0.001830273657105863, + -0.0017032751347869635, -0.006387282162904739, 0.010794879868626595, + -0.020954767242074013, -0.015419123694300652, 0.011960278265178204, + -0.01377561129629612, 0.014276135712862015, -0.028201157227158546, + -0.002046918496489525, -0.00322725810110569, 0.015874823555350304, + 0.021798934787511826, -0.010705234482884407, -0.0036754885222762823, + 0.007194096688181162, 0.0313163585960865, 0.003062907140702009, + -0.015314536169171333, 0.02692370116710663, 0.042962875217199326, + 0.00938295479863882, -0.04484543949365616, 0.046578601002693176, + 0.026236414909362793, -0.018355030566453934, -0.00047811231343075633, + 0.019214140251278877, -0.00611834367737174, -0.0389288030564785, + -0.002793968887999654, -0.012931443750858307, -0.010107593610882759, + -0.0041909534484148026, 0.011840750463306904, -0.0026968521997332573, + -0.031346239149570465, 0.03468555584549904, 0.003197376150637865, + 0.023547032848000526, 0.0007246389868669212, 0.028118979185819626, + 0.007134332321584225, 0.017346512526273727, -0.01574782468378544, + 0.027827631682157516, -0.002913497155532241, 0.0016360405134037137, + -0.011198286898434162, 0.023255683481693268, -0.006745866034179926, + 0.032481756061315536, 0.018355030566453934, 0.016173643991351128, + -0.013140617869794369, -0.05315264314413071, -0.0056103491224348545, + -0.06217701733112335, -0.008479023352265358, -0.043373752385377884, + 0.02527271769940853, -0.039264973253011703, 0.048737574368715286, + -0.016517287120223045, -0.0036007834132760763, -0.008411789312958717, + -0.0382564552128315, 0.017660275101661682, 0.015703001990914345, + -0.0026072063483297825, -0.004138659685850143, -0.01969972252845764, + 0.08213820308446884, -0.03620953857898712, -0.012998678721487522, + -0.011594223789870739, 0.01292397454380989, -0.05907675623893738, + 0.009218603372573853, -0.02578071318566799, 0.020521478727459908, + 0.032758165150880814, -0.010764997452497482, 0.009315719828009605, + -0.017129868268966675, 0.0033841386903077364, -0.011280463077127934, + -0.04391910135746002, 0.025534186512231827, -0.006880335509777069, + 0.007440623361617327, -0.019468136131763458, 0.025713477283716202, + -0.012460802681744099, -0.0057672299444675446, 0.050388555973768234, + -0.0032571402844041586, 0.012251628562808037, -0.0466383621096611, + -0.005998815875500441, 0.0018078621942549944, -0.011116111651062965, + 0.004960415419191122, 0.019610077142715454, -0.006842982489615679, + -0.006566573400050402, 0.01104887668043375, 0.0034588437993079424, + 0.015486356802284718, -0.010137475095689297, -0.005094884429126978, + -0.014007197692990303, -0.01934860832989216, 0.029493553563952446, + -0.05791882425546646, 0.01922907866537571, -0.0032944928389042616, + -0.09949218481779099, -0.02040194906294346, -0.010144946165382862, + -0.022770099341869354, 0.0027192640118300915, -0.012849269434809685, + 0.05047820508480072, 0.032011114060878754, -0.008359495550394058, + -0.010578235611319542, 0.02046918496489525, 0.04862551763653755, + -0.045801665633916855, 0.03130141645669937, 0.03137611970305443, + -0.019811779260635376, 0.02223222330212593, -0.017757391557097435, + 0.01899002306163311, 0.025115838274359703, -0.009599599055945873, + 0.07309142500162125, 0.01588229462504387, 0.008404318243265152, + 0.034775201231241226, -0.027760395780205727, 0.004056484438478947, + -0.0008217555587179959, 0.005326470360159874, 0.0442926250398159, + 0.007119391579180956, -0.03139106184244156, -0.022441396489739418, + 0.008635904639959335, -0.005498291924595833, 0.00540117546916008, + 0.0027491459622979164, -0.02805921621620655, -0.033333394676446915, + -0.03194388002157211, -0.017249396070837975, 0.004758711438626051, + 0.07516822218894958, -0.009278367273509502, 0.020715711638331413, + -0.019953718408942223, 0.01878831908106804, 0.014313487336039543, + -0.022740216925740242, 0.0008740490884520113, 0.019707193598151207, + 0.01625581830739975, -0.0039967200718820095, 0.04726588353514671, + -0.0011653987457975745, -0.015590944327414036, -0.01895267143845558, + 0.02831321209669113, 0.006222930736839771, 0.04343351721763611, + -0.001942331320606172, 0.005498291924595833, -0.022822393104434013, + -0.017503393813967705, 0.00837443582713604, -0.0072239781729876995, + 0.016412699595093727, -0.008366965688765049, 0.027028288692235947, + 0.03430455923080444, 0.025145720690488815, 0.021320821717381477, + -0.09243255853652954, 0.00502764992415905, -0.07131344079971313, + 0.009778891690075397, -0.02291204035282135, -0.00046317133819684386, + -0.004325422458350658, 0.01776486076414585, -0.018743496388196945, + -0.02123117446899414, -0.02457796223461628, -0.03160770609974861, + -0.012879150919616222, -0.027640867978334427, 0.010189768858253956, + 0.06280454248189926, -0.0057224067859351635, 0.010839702561497688, + -0.02601976878941059, 0.00540117546916008, -0.006207989528775215, + 0.05945027619600296, 0.0041909534484148026, -0.02009565941989422, + 0.02253851294517517, 0.015755295753479004, -0.028597094118595123, + -0.004796063993126154, 0.03963850066065788, -0.021036941558122635, + 0.00008217555296141654, -0.0003735252539627254, 0.02180640399456024, + -0.044240329414606094, -0.1282835155725479, -0.027775337919592857, + -0.13690447807312012, 0.026587529107928276, 0.003234728705137968, + -0.00243538455106318, -0.026064591482281685, 0.04545802250504494, + -0.017085043713450432, 0.026161709800362587, -0.005035120528191328, + -0.016076527535915375, -0.020274950191378593, -0.008142850361764431, + 0.016860930249094963, -0.025332482531666756, 0.0396459698677063, + -0.047856055200099945, 0.012565389275550842, 0.0017854507314041257, + -0.010712703689932823, -0.03399079665541649, -0.009808773174881935, + 0.018810732290148735, -0.016412699595093727, -0.0073808589950203896, + 0.0021515055559575558, -0.045577552169561386 + ] + }, + "pets": { + "prompt": "Photo of cute pets", + "vector": [ + -0.018412098288536072, 0.015965646132826805, -0.006039677653461695, + -0.006294516380876303, 0.0009683871758170426, 0.0048355646431446075, + -0.01478064525872469, 0.0024400807451456785, 0.019100161269307137, + -0.009696613065898418, 0.016220485791563988, 0.0063645970076322556, + -0.07371847331523895, -0.009652016684412956, -0.012397903949022293, + 0.5433799028396606, -0.030102822929620743, -0.007033549249172211, + 0.0409589558839798, -0.03465806692838669, 0.02250226028263569, + 0.04019443690776825, 0.03446056693792343, -0.03670952096581459, + 0.011556935496628284, -0.05243943631649017, -0.019686290994286537, + 0.023438790813088417, 0.029172662645578384, 0.004740000236779451, + 0.007995565421879292, -0.03016653284430504, 0.01606758125126362, + 0.015060968697071075, 0.006772339344024658, -0.04906919598579407, + -0.004141129087656736, -0.006090645678341389, -0.016711048781871796, + 0.004548871424049139, 0.05233750119805336, -0.028446370735764503, + -0.00853709690272808, -0.039780326187610626, -0.017061451449990273, + -0.024241533130407333, 0.01281838770955801, 0.004765484016388655, + 0.025745080783963203, -0.011263871565461159, 0.061562661081552505, + 0.016252340748906136, 0.002796855056658387, -0.0019240323454141617, + 0.03179113194346428, 0.022610565647482872, -0.04005427658557892, + -0.02130451612174511, 0.0006753226043656468, -0.01755838841199875, + -0.030956534668803215, 0.019405968487262726, -0.019246693700551987, + 0.04778863117098808, -0.049668069928884506, 0.02699379250407219, + 0.0064537907019257545, 0.04073597118258476, 0.015022742561995983, + 0.01813814602792263, 0.002898790407925844, 0.044456616044044495, + -0.003644193522632122, -0.025394678115844727, -0.0014079839456826448, + -0.006358225829899311, -0.09420750290155411, 0.002554758219048381, + -0.01680024154484272, 0.008728226646780968, 0.0413985475897789, + 0.0026120967231690884, 0.019533388316631317, 0.009550080634653568, + -0.029631372541189194, 0.02370637282729149, -0.0016118548810482025, + 0.014251855202019215, -0.03925790637731552, -0.01651354879140854, + 0.027656372636556625, 0.013888711109757423, -0.004982097074389458, + 0.029771532863378525, -0.03363233804702759, 0.00021661292703356594, + -0.07534944266080856, -0.01572354882955551, 0.0008154839160852134, + -0.036212582141160965, -0.0546119399368763, 0.018373873084783554, + -0.015678953379392624, 0.03827040269970894, -0.006937984377145767, + -0.021221693605184555, -0.06097016483545303, 0.005115887615829706, + 0.003408468095585704, -0.013156048953533173, 0.002592983888462186, + -0.009314354509115219, 0.10348363220691681, -0.01737362891435623, + -0.010684113018214703, -0.00946088694036007, -0.014194516465067863, + 0.0004905645619146526, 0.010199920274317265, -0.0007772581302560866, + -0.009989677928388119, -0.02426701784133911, 0.06381798535585403, + -0.009492741897702217, 0.02280169539153576, -0.003121774410828948, + 0.0017456453060731292, 0.00788088794797659, 0.028548305854201317, + 0.02487863041460514, 0.024279760196805, 0.050165001302957535, + 0.006613065022975206, -0.007555968128144741, -0.024540970101952553, + -0.03653113171458244, -0.02502516098320484, 0.011346694082021713, + -0.007849032990634441, 0.007097258232533932, -0.0324983075261116, + -0.0029242741875350475, 0.02594895288348198, 0.0070526618510484695, + 0.004994838964194059, 0.020590970292687416, 0.021954355761408806, + 0.024916857481002808, -0.0013251613127067685, -0.00966475810855627, + 0.01769217848777771, -0.008530725724995136, -0.007262903265655041, + -0.032918792217969894, -0.00833322573453188, -0.020043065771460533, + -0.009702984243631363, 0.0010766936466097832, 0.015156532637774944, + -0.009104114025831223, -0.058128710836172104, 0.03610427677631378, + 0.005963225848972797, 0.02354072593152523, 0.03976758196949959, + 0.02101145125925541, -0.00396911334246397, -0.5477248430252075, + 0.0277455672621727, -0.005084032658487558, -0.017813226208090782, + 0.06590129435062408, 0.015819113701581955, 0.03328193724155426, + 0.029057985171675682, 0.05862564593553543, -0.004217580892145634, + 0.0317656472325325, 0.003930887207388878, -0.027325082570314407, + -0.01633516140282154, 0.013882339932024479, 0.027847502380609512, + 0.005950483959168196, -0.000700806500390172, 0.003962742164731026, + 0.03163185715675354, 0.007619677577167749, -0.018787983804941177, + -0.0214319359511137, 0.007817178033292294, -0.0603075847029686, + -0.0012805645819753408, -0.012984032742679119, -0.022565970197319984, + -0.03361322730779648, 0.014239112846553326, 0.010633145458996296, + 0.02683451771736145, 0.007785323075950146, 0.061683714389801025, + -0.021183468401432037, -0.012461613863706589, -0.021323630586266518, + -0.028860483318567276, 0.01320701651275158, -0.018214598298072815, + 0.028707580640912056, -0.042660001665353775, 0.020329760387539864, + -0.0006753226043656468, -0.010849758982658386, 0.005351613275706768, + -0.04668645188212395, -0.014258225448429585, 0.01813177578151226, + -0.04093347117304802, -0.03286782279610634, 0.017144273966550827, + -0.00764516182243824, 0.012391532771289349, 0.04016895592212677, + -0.0038990324828773737, 0.007237419486045837, -0.02127903327345848, + -0.01976911351084709, 0.008129355497658253, 0.021846048533916473, + -0.011652500368654728, -0.07947145402431488, -0.0050521777011454105, + 0.00004459677802515216, -0.022846290841698647, -0.06710540503263474, + -0.006294516380876303, -0.015405001118779182, -0.030497824773192406, + -0.013385403901338577, 0.037703387439250946, -0.032326292246580124, + -0.012041130103170872, -0.03310991823673248, -0.015303065069019794, + 0.002389112953096628, -0.03407830744981766, -0.007345725782215595, + 0.0293892752379179, -0.03459435701370239, -0.08546654134988785, + -0.006415564566850662, 0.013009516522288322, -0.04607484117150307, + -0.010849758982658386, 0.0468011312186718, -0.013308952562510967, + -0.005848548375070095, -0.010868871584534645, -0.049559760838747025, + -0.007466774433851242, 0.020801210775971413, -0.012920322827994823, + -0.00475911283865571, 0.02626113034784794, 0.020438065752387047, + 0.006396451964974403, 0.026509597897529602, 0.03451153263449669, + -0.0045934682711958885, 0.0024400807451456785, 0.011671612970530987, + -0.012251371517777443, 0.0025675001088529825, -0.010467500425875187, + 0.01026363018900156, 0.05339508131146431, 0.030045485123991966, + 0.04553968086838722, 0.025088870897889137, -0.0013633872149512172, + -0.042672742158174515, 0.015596129931509495, -0.012404275126755238, + -0.01755201630294323, -0.02296096831560135, 0.030472340062260628, + 0.029886210337281227, -0.0198200810700655, 0.04711968079209328, + 0.006218065042048693, -0.004077419172972441, -0.06715000420808792, + 0.007606935687363148, -0.00536435516551137, 0.029344677925109863, + 0.009938710369169712, 0.008581694215536118, 0.012780161574482918, + 0.02591072767972946, 0.016889436170458794, -0.04043653607368469, + -0.02471298538148403, -0.0017265323549509048, 0.003605967853218317, + 0.02171863056719303, 0.02534371055662632, -0.014455726370215416, + 0.0554911345243454, -0.01784508116543293, 0.0418381467461586, + -0.004599838983267546, 0.04076782613992691, 0.03344758227467537, + 0.04495355114340782, -0.00823766179382801, 0.011805403977632523, + -0.023285888135433197, -0.05006306618452072, 0.0018539517186582088, + -0.05221645534038544, -0.01827193610370159, 0.019278548657894135, + 0.03916871175169945, -0.002223467919975519, 0.016112178564071655, + -0.07009975612163544, -0.023763710632920265, -0.000860080705024302, + -0.011862742714583874, 0.014143547974526882, 0.024993307888507843, + -0.010008790530264378, -0.001688306569121778, -0.010633145458996296, + 0.0553446002304554, -0.007396693807095289, 0.024228790774941444, + -0.02114524319767952, -0.02173137106001377, 0.02101145125925541, + 0.023808307945728302, -0.01453854888677597, 0.00426854845136404, + -0.010798790492117405, -0.013079597614705563, -0.005001210141927004, + 0.0015991129912436008, 0.015131048858165741, -0.013920566067099571, + 0.03167008236050606, 0.00898943655192852, -0.011130081489682198, + 0.0063263713382184505, -0.02939564548432827, -0.053458791226148605, + 0.03219250217080116, 0.011875484138727188, 0.02474484033882618, + -0.04773129150271416, -0.033078063279390335, 0.04755927622318268, + 0.014825242571532726, 0.00277137104421854, -0.028102342039346695, + 0.00401371018961072, 0.039347097277641296, 0.032033227384090424, + -0.005969597026705742, -0.016711048781871796, -0.004899274557828903, + 0.02397395297884941, 0.015264839865267277, -0.02385290339589119, + 0.03400822728872299, -0.027452502399683, 0.02171863056719303, + -0.06312992423772812, 0.01647532358765602, -0.013977904804050922, + -0.05996992439031601, -0.004567984025925398, -0.00021661292703356594, + 0.010301855392754078, -0.007078145164996386, -0.024470888078212738, + 0.030676212161779404, 0.009957822971045971, -0.0411054864525795, + -0.033065322786569595, 0.035607341676950455, 0.010894355364143848, + -0.07190274447202682, -0.03331379219889641, 0.022540485486388206, + 0.008887500502169132, 0.005485403351485729, -0.01666645146906376, + -0.0017010484589263797, 0.038002822548151016, -0.028733065351843834, + -0.07052024453878403, 0.033078063279390335, -0.0019877420272678137, + -0.005663790740072727, -0.027675487101078033, 0.024407178163528442, + 0.02864387072622776, 0.009473629295825958, -0.043851371854543686, + 0.05092315003275871, 0.015398629941046238, -0.02924911305308342, + -0.023126613348722458, 0.03863992169499397, -0.00818669330328703, + 0.00558733893558383, 0.02818516455590725, -0.02622290514409542, + -0.02606363035738468, -0.011894597671926022, -0.01845669560134411, + 0.05558669567108154, -0.06443597376346588, 0.07635605335235596, + 0.010945322923362255, 0.028134196996688843, -0.016163146123290062, + -0.005517258308827877, 0.02263605035841465, 0.011773549020290375, + 0.0020896773785352707, -0.02352161332964897, 0.03303983807563782, + 0.008970323018729687, -0.025076130405068398, -0.04618314653635025, + -0.015252097509801388, -0.010958065278828144, 0.03614887222647667, + -0.018647823482751846, 0.02625476010143757, -0.03058064728975296, + 0.015080081298947334, 0.023171210661530495, -0.032587502151727676, + 0.03588129207491875, -0.019527016207575798, 0.009014920331537724, + -0.0513245165348053, 0.040130726993083954, 0.010212661698460579, + -0.07171798497438431, 0.04889081045985222, -0.018832581117749214, + 0.017915163189172745, -0.010187177918851376, -0.039359841495752335, + -0.05723040550947189, -0.022871773689985275, -0.017335403710603714, + -0.0351995974779129, -0.023725485429167747, -0.027255002409219742, + -0.01102177519351244, 0.019858308136463165, 0.02341330796480179, + 0.02624838799238205, 0.054662905633449554, -0.009282500483095646, + -0.020622823387384415, 0.04590919613838196, -0.05874669924378395, + 0.008250403217971325, 0.019450565800070763, -0.015347662381827831, + 0.01154419407248497, 0.011518710292875767, 0.013614758849143982, + 0.023158468306064606, 0.015226613730192184, -0.03371516242623329, + -0.029478468000888824, 0.018647823482751846, 0.010633145458996296, + 0.0005479032406583428, -0.025146210566163063, -0.03495113179087639, + -0.07967532426118851, 0.01184362918138504, 0.015245726332068443, + 0.006307258270680904, 0.02591709792613983, 0.023298630490899086, + 0.021533871069550514, 0.006549355108290911, -0.010798790492117405, + 0.011047258973121643, -0.019527016207575798, -0.0348237119615078, + -0.009620161727070808, -0.009913226589560509, -0.016322419047355652, + -0.015564274974167347, -0.009199677966535091, -0.023336855694651604, + -0.026057260110974312, -0.0160293560475111, -0.02277621068060398, + 0.005351613275706768, 0.022438550367951393, 0.003586854785680771, + 0.01966717839241028, -0.05842814967036247 + ] + } + } + } +} diff --git a/mobile/apps/photos/lib/services/smart_memories_service.dart b/mobile/apps/photos/lib/services/smart_memories_service.dart index 9583bbc38d..117f334253 100644 --- a/mobile/apps/photos/lib/services/smart_memories_service.dart +++ b/mobile/apps/photos/lib/services/smart_memories_service.dart @@ -35,9 +35,9 @@ import "package:photos/services/collections_service.dart"; import "package:photos/services/language_service.dart"; import "package:photos/services/location_service.dart"; import "package:photos/services/machine_learning/face_ml/person/person_service.dart"; -import "package:photos/services/machine_learning/ml_computer.dart"; import "package:photos/services/machine_learning/ml_result.dart"; import "package:photos/services/search_service.dart"; +import "package:photos/utils/text_embeddings_util.dart"; class MemoriesResult { final List memories; @@ -103,24 +103,18 @@ class SmartMemoriesService { 'allImageEmbeddings has ${allImageEmbeddings.length} entries $t', ); - const String clipPositiveQuery = - 'Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion'; - final clipPositiveTextVector = Vector.fromList( - await MLComputer.instance.runClipText(clipPositiveQuery), - ); - final Map clipPeopleActivityVectors = {}; - for (final peopleActivity in PeopleActivity.values) { - clipPeopleActivityVectors[peopleActivity] ??= Vector.fromList( - await MLComputer.instance.runClipText(activityQuery(peopleActivity)), + // Load pre-computed text embeddings from assets + final textEmbeddings = await loadTextEmbeddingsFromAssets(); + if (textEmbeddings == null) { + _logger.severe('Failed to load pre-computed text embeddings'); + throw Exception( + 'Failed to load pre-computed text embeddings', ); } - final Map clipMemoryTypeVectors = {}; - for (final clipMemoryType in ClipMemoryType.values) { - clipMemoryTypeVectors[clipMemoryType] ??= Vector.fromList( - await MLComputer.instance.runClipText(clipQuery(clipMemoryType)), - ); - } - _logger.info('clipPositiveTextVector and clipPeopleActivityVectors $t'); + _logger.info('Using pre-computed text embeddings from assets'); + final clipPositiveTextVector = textEmbeddings.clipPositiveVector; + final clipPeopleActivityVectors = textEmbeddings.peopleActivityVectors; + final clipMemoryTypeVectors = textEmbeddings.clipMemoryTypeVectors; final local = await getLocale(); final languageCode = local?.languageCode ?? "en"; diff --git a/mobile/apps/photos/lib/utils/text_embeddings_util.dart b/mobile/apps/photos/lib/utils/text_embeddings_util.dart new file mode 100644 index 0000000000..349c284ed4 --- /dev/null +++ b/mobile/apps/photos/lib/utils/text_embeddings_util.dart @@ -0,0 +1,172 @@ +import 'dart:convert'; +import "dart:developer" as dev show log; +import "dart:io" show File; + +import 'package:flutter/services.dart'; +import 'package:logging/logging.dart'; +import 'package:ml_linalg/vector.dart'; +import "package:path_provider/path_provider.dart" + show getExternalStorageDirectory; +import 'package:photos/models/memories/clip_memory.dart'; +import 'package:photos/models/memories/people_memory.dart'; +import "package:photos/services/machine_learning/ml_computer.dart" + show MLComputer; + +final _logger = Logger('TextEmbeddingsUtil'); + +/// Loads pre-computed text embeddings from assets +Future loadTextEmbeddingsFromAssets() async { + try { + _logger.info('Loading text embeddings from assets'); + final jsonString = + await rootBundle.loadString('assets/ml/text_embeddings.json'); + final data = json.decode(jsonString) as Map; + + final embeddings = data['embeddings'] as Map; + + // Parse clip positive embedding + Vector? clipPositiveVector; + final clipPositive = embeddings['clip_positive'] as Map; + final clipPositiveVectorData = + (clipPositive['vector'] as List).cast(); + if (clipPositiveVectorData.isNotEmpty) { + clipPositiveVector = Vector.fromList(clipPositiveVectorData); + } + + // Parse people activities embeddings + final Map peopleActivityVectors = {}; + final peopleActivities = + embeddings['people_activities'] as Map; + for (final activity in PeopleActivity.values) { + final activityName = activity.toString().split('.').last; + if (peopleActivities.containsKey(activityName)) { + final activityData = + peopleActivities[activityName] as Map; + final vector = (activityData['vector'] as List).cast(); + if (vector.isNotEmpty) { + peopleActivityVectors[activity] = Vector.fromList(vector); + } + } + } + + // Parse clip memory types embeddings + final Map clipMemoryTypeVectors = {}; + final clipMemoryTypes = + embeddings['clip_memory_types'] as Map; + for (final memoryType in ClipMemoryType.values) { + final typeName = memoryType.toString().split('.').last; + if (clipMemoryTypes.containsKey(typeName)) { + final typeData = clipMemoryTypes[typeName] as Map; + final vector = (typeData['vector'] as List).cast(); + if (vector.isNotEmpty) { + clipMemoryTypeVectors[memoryType] = Vector.fromList(vector); + } + } + } + + // Check if we have all required embeddings + if (clipPositiveVector == null) { + _logger.severe('Clip positive vector is missing'); + throw Exception('Clip positive vector is missing'); + } + + if (peopleActivityVectors.length != PeopleActivity.values.length) { + _logger.severe('Some people activity vectors are missing'); + throw Exception('Some people activity vectors are missing'); + } + + if (clipMemoryTypeVectors.length != ClipMemoryType.values.length) { + _logger.severe('Some clip memory type vectors are missing'); + throw Exception('Some clip memory type vectors are missing'); + } + + _logger.info('Text embeddings loaded successfully from JSON assets'); + return TextEmbeddings( + clipPositiveVector: clipPositiveVector, + peopleActivityVectors: peopleActivityVectors, + clipMemoryTypeVectors: clipMemoryTypeVectors, + ); + } catch (e, stackTrace) { + _logger.severe('Failed to load text embeddings from JSON', e, stackTrace); + return null; + } +} + +class TextEmbeddings { + final Vector clipPositiveVector; + final Map peopleActivityVectors; + final Map clipMemoryTypeVectors; + + const TextEmbeddings({ + required this.clipPositiveVector, + required this.peopleActivityVectors, + required this.clipMemoryTypeVectors, + }); +} + +/// Helper function to generate text embeddings and save them to a JSON file +/// Run this once to generate the embeddings, then copy the output +/// to assets/ml/text_embeddings.json +Future generateAndSaveTextEmbeddings() async { + final Map embeddingsData = { + 'version': '1.0.0', + 'embeddings': { + 'clip_positive': {}, + 'people_activities': {}, + 'clip_memory_types': {}, + }, + }; + + // Generate clip positive embedding + const String clipPositiveQuery = + 'Photo of a precious and nostalgic memory radiating warmth, vibrant energy, or quiet beauty — alive with color, light, or emotion'; + final clipPositiveVector = + await MLComputer.instance.runClipText(clipPositiveQuery); + embeddingsData['embeddings']['clip_positive'] = { + 'prompt': clipPositiveQuery, + 'vector': clipPositiveVector, + }; + + // Generate people activity embeddings + final peopleActivities = {}; + for (final activity in PeopleActivity.values) { + final activityName = activity.toString().split('.').last; + final prompt = activityQuery(activity); + final vector = await MLComputer.instance.runClipText(prompt); + peopleActivities[activityName] = { + 'prompt': prompt, + 'vector': vector, + }; + } + embeddingsData['embeddings']['people_activities'] = peopleActivities; + + // Generate clip memory type embeddings + final clipMemoryTypes = {}; + for (final memoryType in ClipMemoryType.values) { + final typeName = memoryType.toString().split('.').last; + final prompt = clipQuery(memoryType); + final vector = await MLComputer.instance.runClipText(prompt); + clipMemoryTypes[typeName] = { + 'prompt': prompt, + 'vector': vector, + }; + } + embeddingsData['embeddings']['clip_memory_types'] = clipMemoryTypes; + + // Convert to JSON and log it + final jsonString = const JsonEncoder.withIndent(' ').convert(embeddingsData); + dev.log( + '_generateAndSaveTextEmbeddings: Generated text embeddings JSON', + ); + + final tempDir = await getExternalStorageDirectory(); + final file = File('${tempDir!.path}/text_embeddings.json'); + await file.writeAsString(jsonString); + dev.log( + '_generateAndSaveTextEmbeddings: Saved text embeddings to ${file.path}', + ); + + dev.log( + '_generateAndSaveTextEmbeddings: Text embeddings generation complete! Copy the JSON output above to assets/ml/text_embeddings.json', + ); +} diff --git a/mobile/apps/photos/pubspec.yaml b/mobile/apps/photos/pubspec.yaml index e9315cc414..d051633455 100644 --- a/mobile/apps/photos/pubspec.yaml +++ b/mobile/apps/photos/pubspec.yaml @@ -344,6 +344,7 @@ flutter: - assets/image-editor/ - assets/icons/ - assets/launcher_icon/ + - assets/ml/ fonts: - family: Inter fonts: From b19814dd2c89c77702708ad3db5d4516b7f9f4ef Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 14 Aug 2025 21:18:53 +0530 Subject: [PATCH 140/164] Add onBoardingBodyColor to CustomColorScheme extension for light and dark themes --- mobile/packages/ui/lib/theme/ente_theme_data.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/packages/ui/lib/theme/ente_theme_data.dart b/mobile/packages/ui/lib/theme/ente_theme_data.dart index 8c2cf18786..0151407250 100644 --- a/mobile/packages/ui/lib/theme/ente_theme_data.dart +++ b/mobile/packages/ui/lib/theme/ente_theme_data.dart @@ -418,6 +418,10 @@ extension CustomColorScheme on ColorScheme { Color get primaryColor => brightness == Brightness.light ? const Color(0xFF9610D6) : const Color(0xFF9610D6); + + Color get onBoardingBodyColor => brightness == Brightness.light + ? const Color.fromRGBO(0, 0, 0, 0.38) + : const Color.fromRGBO(255, 255, 255, 0.38); EnteTheme get enteTheme => brightness == Brightness.light ? lightTheme : darkTheme; @@ -504,7 +508,8 @@ ThemeData createAppThemeData({ // Create platform-specific typography to ensure consistent font sizes final typography = Typography.material2021( - platform: TargetPlatform.android, // Force Android typography for consistency + platform: + TargetPlatform.android, // Force Android typography for consistency ); return baseThemeData.copyWith( From 20d8a42239d54ed00c7c0eb183e0ef543446a8c1 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Thu, 14 Aug 2025 22:23:50 +0530 Subject: [PATCH 141/164] Add broken-icon --- mobile/packages/accounts/lib/pages/recovery_key_page.dart | 2 ++ mobile/packages/configuration/devtools_options.yaml | 3 +++ mobile/packages/lock_screen/devtools_options.yaml | 3 +++ mobile/packages/network/devtools_options.yaml | 3 +++ mobile/packages/ui/devtools_options.yaml | 3 +++ mobile/packages/utils/devtools_options.yaml | 3 +++ 6 files changed, 17 insertions(+) create mode 100644 mobile/packages/configuration/devtools_options.yaml create mode 100644 mobile/packages/lock_screen/devtools_options.yaml create mode 100644 mobile/packages/network/devtools_options.yaml create mode 100644 mobile/packages/ui/devtools_options.yaml create mode 100644 mobile/packages/utils/devtools_options.yaml diff --git a/mobile/packages/accounts/lib/pages/recovery_key_page.dart b/mobile/packages/accounts/lib/pages/recovery_key_page.dart index bdadbf9466..416d8c4399 100644 --- a/mobile/packages/accounts/lib/pages/recovery_key_page.dart +++ b/mobile/packages/accounts/lib/pages/recovery_key_page.dart @@ -8,6 +8,7 @@ import 'package:ente_configuration/constants.dart'; import 'package:ente_strings/ente_strings.dart'; import 'package:ente_ui/components/buttons/gradient_button.dart'; import 'package:ente_ui/theme/ente_theme.dart'; +import "package:ente_ui/theme/ente_theme_data.dart"; import 'package:ente_ui/utils/toast_util.dart'; import 'package:ente_utils/platform_util.dart'; import 'package:ente_utils/share_utils.dart'; @@ -250,6 +251,7 @@ class _RecoveryKeyPageState extends State { SizedBox( height: 56, child: ElevatedButton( + style: Theme.of(context).colorScheme.optionalActionButtonStyle, onPressed: () async { await _saveKeys(); }, diff --git a/mobile/packages/configuration/devtools_options.yaml b/mobile/packages/configuration/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/mobile/packages/configuration/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/packages/lock_screen/devtools_options.yaml b/mobile/packages/lock_screen/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/mobile/packages/lock_screen/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/packages/network/devtools_options.yaml b/mobile/packages/network/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/mobile/packages/network/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/packages/ui/devtools_options.yaml b/mobile/packages/ui/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/mobile/packages/ui/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/mobile/packages/utils/devtools_options.yaml b/mobile/packages/utils/devtools_options.yaml new file mode 100644 index 0000000000..fa0b357c4f --- /dev/null +++ b/mobile/packages/utils/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: From 5b4ff1d01ab2d3cbd524b4f9c999530185c14aa2 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Fri, 15 Aug 2025 17:34:04 +0530 Subject: [PATCH 142/164] chore: update gradle and kotlin --- .../auth/android/gradle/wrapper/gradle-wrapper.properties | 2 +- mobile/apps/auth/android/settings.gradle | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties index e1ca574ef0..3c85cfe057 100644 --- a/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties +++ b/mobile/apps/auth/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip diff --git a/mobile/apps/auth/android/settings.gradle b/mobile/apps/auth/android/settings.gradle index 748caceba7..3ddaae5aa3 100644 --- a/mobile/apps/auth/android/settings.gradle +++ b/mobile/apps/auth/android/settings.gradle @@ -19,8 +19,8 @@ pluginManagement { plugins { id "dev.flutter.flutter-plugin-loader" version "1.0.0" - id "com.android.application" version "7.3.0" apply false - id "org.jetbrains.kotlin.android" version "1.8.22" apply false + id "com.android.application" version "8.6.0" apply false + id "org.jetbrains.kotlin.android" version "2.1.10" apply false } include ":app" From 3132373c2627af17298a665f742472ca3561f465 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Fri, 15 Aug 2025 18:13:08 +0530 Subject: [PATCH 143/164] chore: update plugins & add desugaring --- mobile/apps/auth/android/app/build.gradle | 8 ++++- mobile/apps/auth/lib/utils/platform_util.dart | 4 +-- mobile/apps/auth/pubspec.lock | 33 ++++++++++--------- mobile/apps/auth/pubspec.yaml | 27 +++++++++------ 4 files changed, 44 insertions(+), 28 deletions(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index 3da027f155..4633c502f3 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -34,6 +34,9 @@ android { ndkVersion flutter.ndkVersion compileOptions { + // Flag to enable support for the new language APIs + coreLibraryDesugaringEnabled true + // Sets Java compatibility to Java 8 sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -115,4 +118,7 @@ flutter { source '../..' } -dependencies {} +dependencies { + // For AGP 7.4+ + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4") +} diff --git a/mobile/apps/auth/lib/utils/platform_util.dart b/mobile/apps/auth/lib/utils/platform_util.dart index bad3c8e738..40ae35ae53 100644 --- a/mobile/apps/auth/lib/utils/platform_util.dart +++ b/mobile/apps/auth/lib/utils/platform_util.dart @@ -59,14 +59,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index b8a9a9f6f0..24dcf85ffe 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -470,10 +470,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "448b1e30142cffe52f37ee085ea9ca50670d5425bb09b649d193549b2dcf6e26" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.3.1" fixnum: dependency: "direct main" description: @@ -485,10 +485,11 @@ packages: fk_user_agent: dependency: "direct main" description: - name: fk_user_agent - sha256: fd6c94e120786985a292d12f61422a581f4e851148d5940af38b819357b8ad0d - url: "https://pub.dev" - source: hosted + path: "." + ref: "458046cd9a88924e5074d96ba45397219d53b230" + resolved-ref: "458046cd9a88924e5074d96ba45397219d53b230" + url: "https://github.com/flutter-fast-kit/fk_user_agent" + source: git version: "2.1.0" flutter: dependency: "direct main" @@ -1081,10 +1082,11 @@ packages: move_to_background: dependency: "direct main" description: - name: move_to_background - sha256: "00caad17a6ce149910777131503f43f8ed80025681f94684e3a6a87d979b914c" - url: "https://pub.dev" - source: hosted + path: "." + ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d" + resolved-ref: "91e4d1a9c55b28bf93425d1f12faf410efc1e48d" + url: "https://github.com/Sayegh7/move_to_background" + source: git version: "1.0.2" native_dio_adapter: dependency: "direct main" @@ -1329,11 +1331,12 @@ packages: qr_code_scanner: dependency: "direct main" description: - name: qr_code_scanner - sha256: f23b68d893505a424f0bd2e324ebea71ed88465d572d26bb8d2e78a4749591fd - url: "https://pub.dev" - source: hosted - version: "1.0.1" + path: "." + ref: a2d31633d4744f72ada87cfa85d221358ab082af + resolved-ref: a2d31633d4744f72ada87cfa85d221358ab082af + url: "https://github.com/juliuscanute/qr_code_scanner" + source: git + version: "1.0.0" qr_flutter: dependency: "direct main" description: diff --git a/mobile/apps/auth/pubspec.yaml b/mobile/apps/auth/pubspec.yaml index 4feeb58eb4..03afdaad57 100644 --- a/mobile/apps/auth/pubspec.yaml +++ b/mobile/apps/auth/pubspec.yaml @@ -1,4 +1,3 @@ - name: ente_auth description: ente two-factor authenticator version: 4.4.3+443 @@ -8,12 +7,12 @@ environment: sdk: ">=3.0.0 <4.0.0" dependencies: - adaptive_theme: ^3.1.0 # done + adaptive_theme: ^3.1.0 app_links: ^6.3.3 archive: ^4.0.7 auto_size_text: ^3.0.0 base32: ^2.1.3 - bip39: ^1.0.6 #done + bip39: ^1.0.6 bloc: ^9.0.0 clipboard: ^0.1.3 collection: ^1.18.0 # dart @@ -34,10 +33,12 @@ dependencies: ffi: ^2.1.0 figma_squircle: ^0.6.3 file_picker: ^10.2.0 - # https://github.com/incrediblezayed/file_saver/issues/86 - file_saver: ^0.3.0 + file_saver: ^0.3.1 fixnum: ^1.1.0 - fk_user_agent: ^2.1.0 + fk_user_agent: # no package updates on pub.dev + git: + url: https://github.com/flutter-fast-kit/fk_user_agent + ref: 458046cd9a88924e5074d96ba45397219d53b230 flutter: sdk: flutter flutter_animate: ^4.1.0 @@ -53,7 +54,7 @@ dependencies: path: flutter_inappwebview ref: 3e6c4c4a25340cd363af9d38891d88498b90be26 flutter_launcher_icons: ^0.14.1 - flutter_local_authentication: + flutter_local_authentication: # linux fprintd fix is not published on pub.dev git: url: https://github.com/eaceto/flutter_local_authentication ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 @@ -77,7 +78,10 @@ dependencies: local_auth_darwin: ^1.2.2 logging: ^1.0.1 modal_bottom_sheet: ^3.0.0 - move_to_background: ^1.0.2 + move_to_background: # no package updates on pub.dev + git: + url: https://github.com/Sayegh7/move_to_background + ref: 91e4d1a9c55b28bf93425d1f12faf410efc1e48d native_dio_adapter: ^1.4.0 otp: ^3.1.1 package_info_plus: ^8.0.2 @@ -88,8 +92,11 @@ dependencies: pointycastle: ^3.7.3 privacy_screen: ^0.0.6 protobuf: ^4.1.0 - qr_code_scanner: ^1.0.1 - qr_flutter: ^4.1.0 + qr_code_scanner: # no package updates on pub.dev + git: + url: https://github.com/juliuscanute/qr_code_scanner + ref: a2d31633d4744f72ada87cfa85d221358ab082af + qr_flutter: ^4.1.0 sentry: ^8.14.2 sentry_flutter: ^8.14.2 share_plus: ^11.0.0 From 4c31a7bcd6efc21a6e61bf641ae606c76119e6a1 Mon Sep 17 00:00:00 2001 From: Keerthana Date: Sun, 17 Aug 2025 23:55:43 +0530 Subject: [PATCH 144/164] [docs] update bucket configuration --- docs/docs/cli/index.md | 0 .../administration/object-storage.md | 27 ++++++++++++------- docs/docs/self-hosting/installation/config.md | 12 ++++----- server/config/example.yaml | 7 +++++ server/configurations/local.yaml | 7 +++++ server/quickstart.sh | 11 ++++++++ 6 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 docs/docs/cli/index.md diff --git a/docs/docs/cli/index.md b/docs/docs/cli/index.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/docs/self-hosting/administration/object-storage.md b/docs/docs/self-hosting/administration/object-storage.md index f92d6eae40..15345104c9 100644 --- a/docs/docs/self-hosting/administration/object-storage.md +++ b/docs/docs/self-hosting/administration/object-storage.md @@ -63,16 +63,25 @@ It has no relation to Backblaze, Wasabi or Scaleway. Each bucket's endpoint, region, key and secret should be configured accordingly if using an external bucket. -A sample configuration for `b2-eu-cen` is provided, which can be used for other -2 buckets as well: +If a bucket has SSL support enabled, set `s3.are_local_buckets` to `false`. Enable path-style URL by setting `s3.use_path_style_urls` to `true`. -```yaml -b2-eu-cen: - key: - secret: - endpoint: localhost:3200 - region: eu-central-2 - bucket: b2-eu-cen +::: note + +You can configure this for individual buckets over defining top-level configuration if you are using the latest server image (August 2025) + +::: + +A sample configuration for `b2-eu-cen` is provided, which can be used for other 2 buckets as well: + +``` yaml + b2-eu-cen: + are_local_buckets: true + use_path_style_urls: true + key: + secret: + endpoint: localhost:3200 + region: eu-central-2 + bucket: b2-eu-cen ``` ## CORS (Cross-Origin Resource Sharing) diff --git a/docs/docs/self-hosting/installation/config.md b/docs/docs/self-hosting/installation/config.md index 7156046c73..a5bc6b3c7d 100644 --- a/docs/docs/self-hosting/installation/config.md +++ b/docs/docs/self-hosting/installation/config.md @@ -96,8 +96,8 @@ provide correct credentials for proper connectivity within Museum. The `s3` section within `museum.yaml` is by default configured to use local MinIO buckets when using `quickstart.sh` or Docker Compose. -If you wish to use an external S3 provider, you can edit the configuration with -your provider's credentials, and set `s3.are_local_buckets` to `false`. +If you wish to use an external S3 provider with SSL, you can edit the configuration with +your provider's credentials, and set `s3.are_local_buckets` to `false`. Additionally, you can configure this for specific buckets in the corresponding bucket sections in the Compose file. If you are using default MinIO, it is accessible at port `3200`. Web Console can be accessed by enabling port `3201` in the Compose file. @@ -111,11 +111,11 @@ and [troubleshooting](/self-hosting/troubleshooting/uploads) sections. | Variable | Description | Default | | -------------------------------------- | -------------------------------------------- | ------- | -| `s3.b2-eu-cen` | Primary hot storage S3 config | | +| `s3.b2-eu-cen` | Primary hot storage bucket configuration | | | `s3.wasabi-eu-central-2-v3.compliance` | Whether to disable compliance lock on delete | `true` | -| `s3.scw-eu-fr-v3` | Optional secondary S3 config | | -| `s3.wasabi-eu-central-2-derived` | Derived data storage | | -| `s3.are_local_buckets` | Use local MinIO-compatible storage | `false` | +| `s3.scw-eu-fr-v3` | Cold storage bucket configuration | | +| `s3.wasabi-eu-central-2-v3` | Secondary hot storage configuration | | +| `s3.are_local_buckets` | | `true` | | `s3.use_path_style_urls` | Enable path-style URLs for MinIO | `false` | ### Encryption Keys diff --git a/server/config/example.yaml b/server/config/example.yaml index 0fbd2f61b7..156f613d6e 100644 --- a/server/config/example.yaml +++ b/server/config/example.yaml @@ -23,12 +23,17 @@ s3: # Only path-style URL works if disabling are_local_buckets with MinIO use_path_style_urls: true b2-eu-cen: + # Uncomment the below configuration to override the top-level configuration + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: localhost:3200 region: eu-central-2 bucket: b2-eu-cen wasabi-eu-central-2-v3: + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: localhost:3200 @@ -36,6 +41,8 @@ s3: bucket: wasabi-eu-central-2-v3 compliance: false scw-eu-fr-v3: + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: localhost:3200 diff --git a/server/configurations/local.yaml b/server/configurations/local.yaml index 70d7589280..388c1a80a5 100644 --- a/server/configurations/local.yaml +++ b/server/configurations/local.yaml @@ -135,12 +135,17 @@ s3: # primary: b2-eu-cen # secondary: wasabi-eu-central-2-v3 b2-eu-cen: + # Uncomment to override top-level bucket configuration for URL addressing and external buckets + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: region: bucket: wasabi-eu-central-2-v3: + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: @@ -152,6 +157,8 @@ s3: # Currently this flag is only honoured for the Wasabi v3 bucket. compliance: true scw-eu-fr-v3: + # are_local_buckets: true + # use_path_style_urls: true key: secret: endpoint: diff --git a/server/quickstart.sh b/server/quickstart.sh index a656437917..6b042a728b 100755 --- a/server/quickstart.sh +++ b/server/quickstart.sh @@ -167,14 +167,23 @@ db: password: $pg_pass s3: + # Top-level configuration for buckets, you can override by specifying these configuration in the desired bucket. + # Set this to false if using external object storage bucket or bucket with SSL are_local_buckets: true + # Set this to false if using subdomain-style URL. This is set to true for ensuring compatibility with MinIO when SSL is enabled. + use_path_style_urls: true b2-eu-cen: + # Uncomment the below configuration to override the top-level configuration + # are_local_buckets: true + # use_path_style_urls: true key: $minio_user secret: $minio_pass endpoint: localhost:3200 region: eu-central-2 bucket: b2-eu-cen wasabi-eu-central-2-v3: + # are_local_buckets: true + # use_path_style_urls: true key: $minio_user secret: $minio_pass endpoint: localhost:3200 @@ -182,6 +191,8 @@ s3: bucket: wasabi-eu-central-2-v3 compliance: false scw-eu-fr-v3: + # are_local_buckets: true + # use_path_style_urls: true key: $minio_user secret: $minio_pass endpoint: localhost:3200 From a52cebf0e539fe1b2e86369b39ae1f829069d930 Mon Sep 17 00:00:00 2001 From: Keerthana Date: Mon, 18 Aug 2025 00:49:29 +0530 Subject: [PATCH 145/164] [cli] remove dead code in init --- cli/cmd/root.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cli/cmd/root.go b/cli/cmd/root.go index 782a89b8a8..7db33d5dea 100644 --- a/cli/cmd/root.go +++ b/cli/cmd/root.go @@ -2,11 +2,12 @@ package cmd import ( "fmt" - "github.com/ente-io/cli/pkg" - "github.com/spf13/cobra/doc" "os" "runtime" + "github.com/ente-io/cli/pkg" + "github.com/spf13/cobra/doc" + "github.com/spf13/viper" "github.com/spf13/cobra" @@ -20,11 +21,6 @@ var ctrl *pkg.ClICtrl var rootCmd = &cobra.Command{ Use: "ente", Short: "CLI tool for exporting your photos from ente.io", - // Uncomment the following line if your bare application - // has an action associated with it: - Run: func(cmd *cobra.Command, args []string) { - fmt.Sprintf("Hello World") - }, } func GenerateDocs() error { From bc70b4e725251281ad80499e1ecb5b68d2e92ecf Mon Sep 17 00:00:00 2001 From: Keerthana Date: Mon, 18 Aug 2025 01:52:26 +0530 Subject: [PATCH 146/164] [docs] fix linting for self-hosting --- .../administration/object-storage.md | 18 +++++++++--------- docs/docs/self-hosting/installation/config.md | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/docs/self-hosting/administration/object-storage.md b/docs/docs/self-hosting/administration/object-storage.md index 15345104c9..1ff597facb 100644 --- a/docs/docs/self-hosting/administration/object-storage.md +++ b/docs/docs/self-hosting/administration/object-storage.md @@ -73,15 +73,15 @@ You can configure this for individual buckets over defining top-level configurat A sample configuration for `b2-eu-cen` is provided, which can be used for other 2 buckets as well: -``` yaml - b2-eu-cen: - are_local_buckets: true - use_path_style_urls: true - key: - secret: - endpoint: localhost:3200 - region: eu-central-2 - bucket: b2-eu-cen +```yaml +b2-eu-cen: + are_local_buckets: true + use_path_style_urls: true + key: + secret: + endpoint: localhost:3200 + region: eu-central-2 + bucket: b2-eu-cen ``` ## CORS (Cross-Origin Resource Sharing) diff --git a/docs/docs/self-hosting/installation/config.md b/docs/docs/self-hosting/installation/config.md index a5bc6b3c7d..c406d77fb6 100644 --- a/docs/docs/self-hosting/installation/config.md +++ b/docs/docs/self-hosting/installation/config.md @@ -111,11 +111,11 @@ and [troubleshooting](/self-hosting/troubleshooting/uploads) sections. | Variable | Description | Default | | -------------------------------------- | -------------------------------------------- | ------- | -| `s3.b2-eu-cen` | Primary hot storage bucket configuration | | +| `s3.b2-eu-cen` | Primary hot storage bucket configuration | | | `s3.wasabi-eu-central-2-v3.compliance` | Whether to disable compliance lock on delete | `true` | -| `s3.scw-eu-fr-v3` | Cold storage bucket configuration | | -| `s3.wasabi-eu-central-2-v3` | Secondary hot storage configuration | | -| `s3.are_local_buckets` | | `true` | +| `s3.scw-eu-fr-v3` | Cold storage bucket configuration | | +| `s3.wasabi-eu-central-2-v3` | Secondary hot storage configuration | | +| `s3.are_local_buckets` | | `true` | | `s3.use_path_style_urls` | Enable path-style URLs for MinIO | `false` | ### Encryption Keys From eecd7ed355b574b1b73970a89508fa96bad33c1a Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Aug 2025 00:43:39 +0000 Subject: [PATCH 147/164] New Crowdin translations by GitHub Action --- .../base/locales/fr-FR/translation.json | 24 ++++++++--------- .../base/locales/lt-LT/translation.json | 24 ++++++++--------- .../base/locales/ru-RU/translation.json | 24 ++++++++--------- .../base/locales/vi-VN/translation.json | 24 ++++++++--------- .../base/locales/zh-CN/translation.json | 26 +++++++++---------- 5 files changed, 61 insertions(+), 61 deletions(-) diff --git a/web/packages/base/locales/fr-FR/translation.json b/web/packages/base/locales/fr-FR/translation.json index 49522f9ec7..11b5a723c4 100644 --- a/web/packages/base/locales/fr-FR/translation.json +++ b/web/packages/base/locales/fr-FR/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Favoris partagés", "added_by_name": "Ajouté par {{name}}", "unowned_files_not_processed": "Les fichiers ajoutés par d'autres utilisateurs n'ont pas été traités", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Domaines personnalisés", + "custom_domains_desc": "Utiliser votre propre domaine lors du partage", + "link_your_domain": "Lier votre domaine", + "domain": "Domaine", + "domain_help": "N'importe quel domaine ou sous-domaine que vous possédez", + "invalid_domain": "Domaine non valide", + "already_linked_domain": "Le domaine déjà lié par un utilisateur", + "add_dns_entry": "Ajouter l'entrée DNS", + "add_dns_entry_hint": "Chez votre fournisseur DNS, ajoutez un CNAME de votre domaine {{host}}", + "custom_domains_help": "Pour plus d'informations, voir l'help", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/lt-LT/translation.json b/web/packages/base/locales/lt-LT/translation.json index 924af32e1e..91d3494e06 100644 --- a/web/packages/base/locales/lt-LT/translation.json +++ b/web/packages/base/locales/lt-LT/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Bendrinami mėgstami", "added_by_name": "Įtraukė {{name}}", "unowned_files_not_processed": "Kiti naudotojai pridėti failai nebuvo apdoroti", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Pasirinktiniai domenai", + "custom_domains_desc": "Naudokite savo domeną, kai bendrinate.", + "link_your_domain": "Susieti savo domeną", + "domain": "Domenas", + "domain_help": "Bet kuris jūsų turimas domenas arba subdomenas", + "invalid_domain": "Netinkamas domenas.", + "already_linked_domain": "Domenas jau susietas su naudotoju.", + "add_dns_entry": "Pridėti DNS elementą", + "add_dns_entry_hint": "Savo DNS teikėjoje pridėkite „CNAME“ iš savo domeno į {{host}}.", + "custom_domains_help": "Dėl daugiau informacijos žiūrėkite pagalbą.", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/ru-RU/translation.json b/web/packages/base/locales/ru-RU/translation.json index 7218c486f7..fe0728c1f1 100644 --- a/web/packages/base/locales/ru-RU/translation.json +++ b/web/packages/base/locales/ru-RU/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Общие избранные", "added_by_name": "Добавлено {{name}}", "unowned_files_not_processed": "Файлы, добавленные другими пользователями, не были обработаны", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Пользовательские домены", + "custom_domains_desc": "Используйте свой собственный домен при публикации", + "link_your_domain": "Привязать ваш домен", + "domain": "Домен", + "domain_help": "Любой домен или поддомен, которым вы владеете", + "invalid_domain": "Неверный домен", + "already_linked_domain": "Домен уже связан пользователем", + "add_dns_entry": "Добавить DNS-запись", + "add_dns_entry_hint": "На стороне вашего DNS-провайдера добавьте CNAME из вашего домена в {{host}}", + "custom_domains_help": "Для получения дополнительной информации см. помощь", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/vi-VN/translation.json b/web/packages/base/locales/vi-VN/translation.json index 6cf4877fd5..af041df4a8 100644 --- a/web/packages/base/locales/vi-VN/translation.json +++ b/web/packages/base/locales/vi-VN/translation.json @@ -686,16 +686,16 @@ "shared_favorites": "Những mục thích đã chia sẻ", "added_by_name": "Được thêm bởi {{name}}", "unowned_files_not_processed": "Các tệp được thêm bởi người dùng khác không được xử lý", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "Tùy chỉnh tên miền", + "custom_domains_desc": "Dùng tên miền của bạn khi chia sẻ", + "link_your_domain": "Liên kết với tên miền", + "domain": "Tên miền", + "domain_help": "Bất kỳ tên miền hoặc tên miền phụ nào của bạn", + "invalid_domain": "Tên miền không hợp lệ", + "already_linked_domain": "Tên miền đã được liên kết với người dùng khác", + "add_dns_entry": "Thêm entry DNS", + "add_dns_entry_hint": "Trên trình quản lý của nhà cung cấp DNS, thêm một CNAME từ tên miền của bạn đến {{host}}", + "custom_domains_help": "Để biết thêm chi tiết, xem trợ giúp", + "num_1": "1", + "num_2": "2" } diff --git a/web/packages/base/locales/zh-CN/translation.json b/web/packages/base/locales/zh-CN/translation.json index 655aeae950..16b109545f 100644 --- a/web/packages/base/locales/zh-CN/translation.json +++ b/web/packages/base/locales/zh-CN/translation.json @@ -1,5 +1,5 @@ { - "intro_slide_1_title": "私人备份
为您的回忆", + "intro_slide_1_title": "为您的回忆
私密备份", "intro_slide_1": "默认端到端加密", "intro_slide_2_title": "安全地存放
在一个掩护所中", "intro_slide_2": "经久耐用", @@ -686,16 +686,16 @@ "shared_favorites": "已共享的收藏", "added_by_name": "由{{name}}添加", "unowned_files_not_processed": "由其他用户添加的文件未被处理", - "custom_domains": "", - "custom_domains_desc": "", - "link_your_domain": "", - "domain": "", - "domain_help": "", - "invalid_domain": "", - "already_linked_domain": "", - "add_dns_entry": "", - "add_dns_entry_hint": "", - "custom_domains_help": "", - "num_1": "", - "num_2": "" + "custom_domains": "自定义域名", + "custom_domains_desc": "分享时使用您自己的域名", + "link_your_domain": "链接您的域名", + "domain": "域名", + "domain_help": "您拥有的任何域名或子域名", + "invalid_domain": "无效的域名", + "already_linked_domain": "域名已被其他用户链接", + "add_dns_entry": "添加 DNS 条目", + "add_dns_entry_hint": "在您的 DNS 提供商上,将您域中的 CNAME 添加到 {{host}}", + "custom_domains_help": "欲了解更多信息,请参阅 help", + "num_1": "1", + "num_2": "2" } From e82ba882d654eeeee0099f23f58cd220a677f6ba Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Aug 2025 01:05:17 +0000 Subject: [PATCH 148/164] New Crowdin translations by GitHub Action --- .../metadata/android/id/full_description.txt | 4 +- mobile/apps/photos/lib/l10n/intl_de.arb | 8 + mobile/apps/photos/lib/l10n/intl_fr.arb | 53 +++- mobile/apps/photos/lib/l10n/intl_no.arb | 30 ++- mobile/apps/photos/lib/l10n/intl_sr.arb | 227 ++++++++++++++++++ mobile/apps/photos/lib/l10n/intl_vi.arb | 4 +- mobile/apps/photos/lib/l10n/intl_zh.arb | 51 +++- 7 files changed, 369 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt b/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt index 7b3deb7985..1f79440d32 100644 --- a/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt +++ b/mobile/apps/photos/fastlane/metadata/android/id/full_description.txt @@ -16,7 +16,7 @@ FITUR - Collaborative albums, so you can pool together photos after a trip - Shared folders, in case you want your partner to enjoy your "Camera" clicks - Link album, yang bisa dilindungi dengan sandi -- Ability to free up space, by removing files that have been safely backed up +Kemampuan untuk membebaskan kapasitas, dengan menghilangkan files yang sudah di back-up dengan aman - Human support, because you're worth it - Descriptions, so you can caption your memories and find them easily - Editor gambar, untuk menyempurnakan fotomu @@ -33,4 +33,4 @@ HARGA Kami tidak menyediakan paket yang gratis seumur hidup, karena penting bagi kami untuk tetap berdiri dan bertahan hingga masa depan. Namun, kami menyediakan paket yang terjangkau, yang bisa kamu bagikan dengan keluargamu. Kamu bisa menemukan informasi lebih lanjut di ente.io. DUKUNGAN -We take pride in offering human support. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam. +Kita bangga untuk menyediakan support berbasis manusia. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam. diff --git a/mobile/apps/photos/lib/l10n/intl_de.arb b/mobile/apps/photos/lib/l10n/intl_de.arb index afba351302..8849255874 100644 --- a/mobile/apps/photos/lib/l10n/intl_de.arb +++ b/mobile/apps/photos/lib/l10n/intl_de.arb @@ -1776,6 +1776,14 @@ "same": "Gleich", "different": "Verschieden", "sameperson": "Dieselbe Person?", + "cLTitle1": "Erweiterte Bildbearbeitung", + "cLDesc1": "Wir veröffentlichen eine neue und erweiterte Bildbearbeitung, die mehr Bildzuschnitte ermöglicht, vordefinierte Filter für schnelleres Bearbeiten bietet, sowie die Feinabstimmung von Sättigung, Kontrast, Helligkeit und vielem mehr erlaubt. Der neue Editor erlaubt außerdem das Zeichnen auf den Fotos oder das Hinzufügen von Emojis als Sticker.", + "cLTitle2": "Intelligente Alben", + "cLDesc2": "Du kannst jetzt automatisch Fotos von ausgewählten Personen zu jedem Album hinzufügen. Öffne einfach das Album und wähle \"Personen automatisch hinzufügen\" aus dem Menü. Zusammen mit einem geteilten Album kannst Du Fotos mit null Klicks teilen.", + "cLTitle3": "Verbesserte Galerie", + "cLDesc3": "Wir haben die Möglichkeit hinzugefügt, Alben nach Wochen, Monaten und Jahren zu gruppieren. Du kannst jetzt die Galerie mit diesen neuen Gruppierungs-Optionen so anpassen, dass sie genau so aussieht, wie Du möchtest, zusammen mit angepassten Rastern", + "cLTitle4": "Schnelleres Scrollen", + "cLDesc4": "Zusammen mit einem Schwung Änderungen unter der Haube, um das Erlebnis beim Scrollen der Galerie zu verbessern, haben wir außerdem den Scrollbalken mit Markern neu gestaltet, um es Dir zu ermöglichen, schnell in der Zeitleiste zu springen.", "indexingPausedStatusDescription": "Die Indizierung ist pausiert. Sie wird automatisch fortgesetzt, wenn das Gerät bereit ist. Das Gerät wird als bereit angesehen, wenn sich der Akkustand, die Akkugesundheit und der thermische Zustand in einem gesunden Bereich befinden.", "thisWeek": "Diese Woche", "lastWeek": "Letzte Woche", diff --git a/mobile/apps/photos/lib/l10n/intl_fr.arb b/mobile/apps/photos/lib/l10n/intl_fr.arb index 8f73c4baa1..7bdb63cbd4 100644 --- a/mobile/apps/photos/lib/l10n/intl_fr.arb +++ b/mobile/apps/photos/lib/l10n/intl_fr.arb @@ -1758,7 +1758,7 @@ "wishThemAHappyBirthday": "Souhaitez à {name} un joyeux anniversaire ! 🎉", "areYouSureRemoveThisFaceFromPerson": "Êtes-vous sûr de vouloir retirer ce visage de cette personne ?", "otherDetectedFaces": "Autres visages détectés", - "areThey": "Vraiment", + "areThey": "S'agit-il de ", "questionmark": "?", "saveAsAnotherPerson": "Enregistrer comme une autre personne", "showLessFaces": "Afficher moins de visages", @@ -1776,7 +1776,56 @@ "same": "Identique", "different": "Différent(e)", "sameperson": "Même personne ?", + "cLTitle1": "Éditeur d'image avancé", + "cLDesc1": "Nous déployons un nouvel éditeur d'image avancé qui ajoute plus d'options de rognage, des filtres, des préréglages pour des modifications rapides ainsi que des options de réglage fin (la saturation, le contraste, la luminosité, la température et beaucoup plus). Le nouvel éditeur inclut également la possibilité de dessiner sur vos photos et d'ajouter des emojis en tant qu'autocollants.", + "cLTitle2": "Albums Intelligents", + "cLDesc2": "Vous pouvez maintenant ajouter automatiquement des photos de personnes sélectionnées à n'importe quel album. Allez simplement à l'album et sélectionnez \"Ajouter automatiquement des personnes\" dans le menu déroulant. Couplé avec un album partagé, vous pouvez partager des photos en zéro clic.", + "cLTitle3": "Galerie améliorée", + "cLDesc3": "Nous avons ajouté la possibilité de regrouper votre galerie par semaines, mois et années. Vous pouvez maintenant personnaliser votre galerie pour qu'elle soit exactement comme vous le souhaitez avec ces nouvelles options de regroupement, ainsi que des grilles personnalisées", + "cLTitle4": "Défilement plus rapide", + "cLDesc4": "En plus des quelques améliorations pour améliorer l'expérience de défilement de la galerie, nous avons également redessiné la barre de défilement pour afficher des marqueurs, ce qui vous permet de sauter rapidement dans la chronologie.", "indexingPausedStatusDescription": "L'indexation est en pause. Elle reprendra automatiquement lorsque l'appareil sera prêt. Celui-ci est considéré comme prêt lorsque le niveau de batterie, sa santé et son état thermique sont dans une plage saine.", + "thisWeek": "Cette semaine", + "lastWeek": "La semaine dernière", + "thisMonth": "Ce mois", + "thisYear": "Cette année", + "groupBy": "Grouper par", "faceThumbnailGenerationFailed": "Impossible de créer des miniatures de visage", - "fileAnalysisFailed": "Impossible d'analyser le fichier" + "fileAnalysisFailed": "Impossible d'analyser le fichier", + "editAutoAddPeople": "Modifier les personnes auto-ajoutées", + "autoAddPeople": "Ajouter automatiquement des personnes", + "autoAddToAlbum": "Ajouter automatiquement à l'album", + "shouldRemoveFilesSmartAlbumsDesc": "Les fichiers liés à la personne qui ont été précédemment sélectionnés dans les albums intelligents doivent-ils être supprimés ?", + "addingPhotos": "Ajout des photos", + "gettingReady": "Préparation", + "addSomePhotosDesc1": "Ajoutez quelques photos ou choisissez des ", + "addSomePhotosDesc2": "visages familiers", + "addSomePhotosDesc3": "\npour commencer", + "ignorePerson": "Ignorer la personne", + "mixedGrouping": "Plusieurs personnes?", + "analysis": "Analyse", + "doesGroupContainMultiplePeople": "Ce groupement contient-il plusieurs personnes ?", + "automaticallyAnalyzeAndSplitGrouping": "Nous analyserons automatiquement le regroupement pour déterminer s'il y a plusieurs personnes présentes et nous les séparerons à nouveau si tel est le cas. Cela peut prendre quelques secondes.", + "layout": "Disposition", + "day": "Jour", + "peopleAutoAddDesc": "Sélectionnez les personnes que vous souhaitez ajouter automatiquement à l'album", + "undo": "Annuler", + "redo": "Rétablir", + "filter": "Filtres", + "adjust": "Ajuster", + "draw": "Dessiner", + "sticker": "Autocollant", + "brushColor": "Couleur du pinceau", + "font": "Police", + "background": "Arrière-plan", + "align": "Aligner", + "addedToAlbums": "{count, plural, one {}=1{Ajouté avec succès à 1 album} other{Ajouté avec succès à {count} albums}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_no.arb b/mobile/apps/photos/lib/l10n/intl_no.arb index 00306d6d95..4dbf2967c0 100644 --- a/mobile/apps/photos/lib/l10n/intl_no.arb +++ b/mobile/apps/photos/lib/l10n/intl_no.arb @@ -1403,6 +1403,16 @@ "enableMachineLearningBanner": "Aktiver maskinlæring for magisk søk og ansiktsgjenkjenning", "searchDiscoverEmptySection": "Bilder vil vises her når behandlingen og synkronisering er fullført", "searchPersonsEmptySection": "Folk vil vises her når behandling og synkronisering er fullført", + "viewersSuccessfullyAdded": "{count, plural, =0 {La til 0 tilskuere} =1 {La til 1 tilskuer} other {La til {count} tilskuer}}", + "@viewersSuccessfullyAdded": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + }, + "description": "Number of viewers that were successfully added to an album." + }, "collaboratorsSuccessfullyAdded": "{count, plural, =0 {La til 0 samarbeidspartner} =1 {La til 1 samarbeidspartner} other {Lagt til {count} samarbeidspartnere}}", "@collaboratorsSuccessfullyAdded": { "placeholders": { @@ -1478,6 +1488,15 @@ }, "currentlyRunning": "Kjører for øyeblikket", "ignored": "ignorert", + "photosCount": "{count, plural, =0 {0 bilder} =1 {1 bilde} other {{count} bilder}}", + "@photosCount": { + "placeholders": { + "count": { + "type": "int", + "example": "2" + } + } + }, "file": "Fil", "searchSectionsLengthMismatch": "Uoverensstemmelse i seksjonslengde: {snapshotLength} != {searchLength}", "@searchSectionsLengthMismatch": { @@ -1643,6 +1662,7 @@ "@linkPersonCaption": { "description": "Caption for the 'Link person' title. It should be a continuation of the 'Link person' title. Just like how 'Link person' + 'for better sharing experience' forms a proper sentence in English, the combination of these two strings should also be a proper sentence in other languages." }, + "videoStreaming": "Strømbare videoer", "processingVideos": "Behandler videoer", "streamDetails": "Strømmedetaljer", "processing": "Behandler", @@ -1708,5 +1728,13 @@ "moon": "I månelyset", "onTheRoad": "På veien igjen", "food": "Kulinær glede", - "pets": "Pelsvenner" + "pets": "Pelsvenner", + "curatedMemories": "Kuraterte minner", + "widgets": "Moduler", + "memories": "Minner", + "peopleWidgetDesc": "Velg folkene du ønsker å se på din hjemskjerm.", + "albumsWidgetDesc": "Velg albumene du ønsker å se på din hjemskjerm.", + "memoriesWidgetDesc": "Velg typen minner du ønsker å se på din hjemskjerm.", + "smartMemories": "Smarte minner", + "pastYearsMemories": "Tidligere års minner" } \ No newline at end of file diff --git a/mobile/apps/photos/lib/l10n/intl_sr.arb b/mobile/apps/photos/lib/l10n/intl_sr.arb index d516e7cff7..52f1bd951d 100644 --- a/mobile/apps/photos/lib/l10n/intl_sr.arb +++ b/mobile/apps/photos/lib/l10n/intl_sr.arb @@ -141,7 +141,14 @@ "enterThe6digitCodeFromnyourAuthenticatorApp": "Унесите 6-цифрени кôд из\nапликације за аутентификацију", "confirm": "Потврди", "setupComplete": "Постављање завршено", + "saveYourRecoveryKeyIfYouHaventAlready": "Уколико већ нисте, сачувајте кључ за опоравак", + "thisCanBeUsedToRecoverYourAccountIfYou": "Може се користити за опоравак рачуна уколико заборавите други ниво провере идентитета", + "twofactorAuthenticationPageTitle": "Двострука провера идентитета", "lostDevice": "Изгубили сте уређај?", + "verifyingRecoveryKey": "Проверавам кључ за опоравак...", + "recoveryKeyVerified": "Кључ за опоравак је потврђен", + "recoveryKeySuccessBody": "Сјајно! Ваш кључ за опоравак је исправан. Хвала за потврду.\n\nНе заборавите да овај кључ чувате на сигурном месту.", + "invalidRecoveryKey": "Кључ за опоравак који сте унели није исправан. Постарајте се да садржи 24 речи, и проверите да ли сте их правилно уписали.\n\nУколико сте унели стари формат кључа, постарајте се да садржи 64 карактера и додатно проверите да ли је сваки исправно уписан.", "invalidKey": "Неисправан кључ", "tryAgain": "Покушај поново", "viewRecoveryKey": "Погледај кључ за опоравак", @@ -186,10 +193,13 @@ "disableDownloadWarningTitle": "Молим, обратите пажњу", "disableDownloadWarningBody": "Људи и даље могу да направе снимак екрана или да скину ваше фотографије користећи друге апликације", "allowDownloads": "Дозволи преузимање", + "linkExpiry": "Рок важења линка", "linkExpired": "Истекао", "linkEnabled": "Омогућено", "linkNeverExpires": "Никада", + "expiredLinkInfo": "Линк је истекао. Поставите ново време за престанак важности линка или онемогућите његово истицање.", "setAPassword": "Постави лозинку", + "lockButtonLabel": "Закључај", "enterPassword": "Унеси лозинку", "removeLink": "Уклони везу", "manageLink": "Управљај везом", @@ -269,11 +279,19 @@ "codeChangeLimitReached": "Жао нам је, достигли сте максимум броја промена кôда.", "onlyFamilyAdminCanChangeCode": "Молимо вас да контактирате {familyAdminEmail} да бисте променили свој код.", "storageInGB": "{storageAmountInGB} ГБ", + "claimed": "Освојено", + "@claimed": { + "description": "Used to indicate storage claimed, like 10GB Claimed" + }, "details": "Више детаља", "claimMore": "Освоји још!", + "theyAlsoGetXGb": "Они ће такође добити {storageAmountInGB} ГБ", + "freeStorageOnReferralSuccess": "{storageAmountInGB} ГБ сваки пут када се неко претплати на неку од наших понуда и при том унесе ваш код", "claimFreeStorage": "Освоји бесплатан простор на диску", "inviteYourFriends": "Позови своје пријатеље", "referralStep1": "1. Дај овај код својим пријатељима", + "referralStep2": "2. Они се претплате на понуду која се плаћа", + "referralStep3": "3. Обоје добијате {storageInGB} ГБ* бесплатно", "referralsAreCurrentlyPaused": "Препоручивање је тренутно паузирано", "youCanAtMaxDoubleYourStorage": "* У најбољем случају можете удвостручити ваш простор", "faq": "Честа питања", @@ -296,10 +314,13 @@ "deleteAlbum": "Избриши албум", "deleteSharedAlbumDialogBody": "Албум ће бити обрисан за све", "yesRemove": "Да, уклони", + "creatingLink": "Креирам линк...", "removeWithQuestionMark": "Уклони?", + "removeParticipantBody": "{userEmail} ће бити уклоњен из овог дељеног албума.\n\nСве фотографије које су они додали ће такође бити уклоњене", "keepPhotos": "Задржи фотографије", "deletePhotos": "Обриши фотгорафије", "inviteToEnte": "Позови у Енте", + "disableLinkMessage": "Ово ће уклонити јавни линк за приступ \"{albumName}\".", "sharing": "Делим...", "youCannotShareWithYourself": "Не можеш делити сам са собом", "archive": "Архивирај", @@ -397,6 +418,7 @@ "discover_sunset": "Заласци сунца", "discover_hills": "Брда", "discover_greenery": "Зеленило", + "waitingForWifi": "Чекам на бежичну мрежу...", "status": "Статус", "indexedItems": "Индексиране ставке", "pendingItems": "На чекању", @@ -406,6 +428,28 @@ "unselectAll": "Деселектуј све", "selectAll": "Означи све", "skip": "Прескочи", + "updatingFolderSelection": "Освежавам избор фолдера...", + "duplicateItemsGroup": "{count} фајлова, {formattedSize} сваки", + "@duplicateItemsGroup": { + "description": "Display the number of duplicate files and their size", + "type": "text", + "placeholders": { + "count": { + "example": "12", + "type": "int" + }, + "formattedSize": { + "example": "2.3 MB", + "type": "String" + } + } + }, + "showMemories": "Прикажи успомене", + "yearsAgo": "{count, plural, one{{count} година уназад} few {{count} године уназад} other{{count} година уназад}}", + "backupStatus": "Статус резервних копија", + "backupOverMobileData": "Копирај користећи мобилни интернет", + "backupVideos": "Копирај видео снимке", + "disableAutoLock": "Онемогући закључавање екрана", "about": "О нама", "privacy": "Приватност", "terms": "Услови", @@ -413,17 +457,67 @@ "checkStatus": "Провери статус", "checking": "Проверавам...", "youAreOnTheLatestVersion": "Користите најновију верзију", + "account": "Рачун", "manageSubscription": "Управљај претплатом", "changePassword": "Измени лозинку", + "authToChangeYourPassword": "Потврдите идентитет како бисте променили лозинку", + "emailVerificationToggle": "Провера имејла", + "exportYourData": "Извези своје податке", + "logout": "Одјави се", + "authToInitiateAccountDeletion": "Потврдите идентитет како бисте покренули брисање рачуна", + "areYouSureYouWantToLogout": "Да ли сигурно желите да се одјавите?", + "yesLogout": "Да, одјави ме", "aNewVersionOfEnteIsAvailable": "Нова Енте верзија је доступна.", + "update": "Ажурирај", + "installManually": "Инсталирај ручно", + "criticalUpdateAvailable": "Критично ажурирање је доступно", + "updateAvailable": "Ажурирање доступно", + "ignoreUpdate": "Игнориши", + "cannotDeleteSharedFiles": "Не могу да обришем дељене фајлове", "retry": "Покушај поново", "freeUpDeviceSpace": "Ослободи простор на уређају", + "allClear": "✨ Све је чисто", "noDeviceThatCanBeDeleted": "Не постоје фајлови на овом уређају који би могли бити обрисани", "removeDuplicates": "Уклони дупликате", + "removeDuplicatesDesc": "Прегледај и уклони фајлове који су дупликати.", "viewLargeFiles": "Велики фајлови", + "viewLargeFilesDesc": "Погледај фајлове који заузимају највише простора.", + "noDuplicates": "✨ Нема дупликата", "youveNoDuplicateFilesThatCanBeCleared": "Немаш дупликата које бисмо могли да обришемо", "rateUs": "Оцени нас", + "youHaveSuccessfullyFreedUp": "Успешно сте ослободили {storageSaved}!", + "@youHaveSuccessfullyFreedUp": { + "description": "The text to display when the user has successfully freed up storage", + "type": "text", + "placeholders": { + "storageSaved": { + "example": "1.2 GB", + "type": "String" + } + } + }, + "duplicateFileCountWithStorageSaved": "Обрисали сте {count, plural, one{{count} дупликат} few {{count} дупликата} other{{count} дупликата}}, ослобађам ({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": "Породичне понуде", + "referrals": "Препоруке", "notifications": "Обавештења", + "sharedPhotoNotifications": "Нове дељене фотографије", + "sharedPhotoNotificationsExplanation": "Добиј обавештење када неко дода фотографију у албум у којем ви учествујете", + "advanced": "Напредно", + "general": "Опште", "security": "Сигурност", "no": "Не", "yes": "Да", @@ -435,14 +529,19 @@ "matrix": "Матрикс", "discord": "Дискорд", "reddit": "Редит", + "yourStorageDetailsCouldNotBeFetched": "Подаци о вашем простору на диску нису могли бити освежени", "reportABug": "Пријави грешку", "reportBug": "Пријави грешку", + "suggestFeatures": "Предложи нову функционалност", "support": "Подршка", "theme": "Тема", "lightTheme": "Светла", "darkTheme": "Тамна", "systemTheme": "Системска", "freeTrial": "Бесплатна проба", + "selectYourPlan": "Изабери пакет", + "enteSubscriptionPitch": "Енте чува твоје успомене тако да су увек са тобом, чак и ако изгубиш уређај.", + "enteSubscriptionShareWithFamily": "Можеш додати и породицу у оквиру овог пакета.", "currentUsageIs": "Тренутно искоришћено", "@currentUsageIs": { "description": "This text is followed by storage usage", @@ -452,7 +551,12 @@ "type": "text" }, "faqs": "Често постављана питања", + "renewsOn": "Претплата се обнавља {endDate}", "freeTrialValidTill": "Бесплатан пробни период важи до {endDate}", + "validTill": "Важи до {endDate}", + "addOnValidTill": "Ваш {storageAmount} додатак важи до {endDate}", + "playStoreFreeTrialValidTill": "Бесплатни пробни период важи до {endDate}.\nМожете изабрати неки од планова након тога.", + "subWillBeCancelledOn": "Ваша претплата ће бити отказана {endDate}", "subscription": "Претплата", "paymentDetails": "Детаљи плаћања", "manageFamily": "Управљај породицом", @@ -462,12 +566,119 @@ "yesRenew": "Да, обнови претплату", "areYouSureYouWantToCancel": "Да ли сте сигурни да желите да откажете?", "yesCancel": "Да, откажи", + "failedToCancel": "Неуспешно отказивање", + "twoMonthsFreeOnYearlyPlans": "2 месеца гратис уз годишњу претплату", + "monthly": "Месечно", + "@monthly": { + "description": "The text to display for monthly plans", + "type": "text" + }, + "yearly": "Годишње", + "@yearly": { + "description": "The text to display for yearly plans", + "type": "text" + }, + "confirmPlanChange": "Потврди промену пакета", + "areYouSureYouWantToChangeYourPlan": "Да ли сигурно желиш да промениш пакет?", + "youCannotDowngradeToThisPlan": "Не можете изабрати овај пакет", + "cancelOtherSubscription": "Молимо, прво откажите претплау на {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": "Опционо, коико год кратко желите...", + "send": "Пошаљи", + "askCancelReason": "Отказали сте претплату. Да ли бисте нам рекли разлог?", + "thankYouForSubscribing": "Хвала што сте се претплатили!", + "yourPurchaseWasSuccessful": "Куповина успешно закључена", + "yourPlanWasSuccessfullyUpgraded": "Успешно сте прешли на јачи пакет", + "yourPlanWasSuccessfullyDowngraded": "Успешно сте прешли на слабији пакет", + "yourSubscriptionWasUpdatedSuccessfully": "Претплата успешно ажурирана", + "googlePlayId": "Гугл плеј идентификација", + "appleId": "Епл идентификација", + "playstoreSubscription": "Претплата путем Гугл продавнице", + "appstoreSubscription": "Претплата путем Епл продавнице", + "visitWebToManage": "Претплатом можете управљати на страници web.ente.io", + "couldNotUpdateSubscription": "Неуспешно ажурирање претплате", + "pleaseContactSupportAndWeWillBeHappyToHelp": "Контактирајте подршку на support@ente.io и радо ћемо вам помоћи!", + "paymentFailed": "Неуспела уплата", + "continueOnFreeTrial": "Настави бесплатни пробни период", + "areYouSureYouWantToExit": "Сигурно желите да изађете?", + "thankYou": "Хвала вам", + "pleaseWaitForSometimeBeforeRetrying": "Мало сачекајте пре него што покушате поново", + "youAreOnAFamilyPlan": "Ви сте на породичном пакету!", + "leaveFamily": "Напусти породицу", + "areYouSureThatYouWantToLeaveTheFamily": "Да ли заиста желиш да напустиш породични пакет?", + "leave": "Напусти", + "rateTheApp": "Оцените апликацију", + "startBackup": "Започни сигурносно копирање", + "preserveMore": "Сачувај још", + "grantFullAccessPrompt": "Дозволите приступ свим фотографијама у подешавањима уређаја", + "allowPermTitle": "Дозволи приступ фотографијама", "openSettings": "Отвори подешавања", "selectMorePhotos": "Изабери још фотографија", "existingUser": "Постојећи корисник", + "privateBackups": "Приватни бекапи", "forYourMemories": "за твоје успомене", + "safelyStored": "Сигурно похрањено", "atAFalloutShelter": "у подземном бункеру", + "designedToOutlive": "Направљено да надживи", + "available": "Доступно", + "everywhere": "свуда", + "newToEnte": "Нови на Енте", + "pleaseLoginAgain": "Молимо, улогујте се поново", + "autoLogoutMessage": "Излоговани сте услед техничке грешке. Извињавамо се за непријатност.", + "yourSubscriptionHasExpired": "Ваша претплата је истекла", + "storageLimitExceeded": "Расположив простор је прекорачен", + "upgrade": "Ажурирај", + "raiseTicket": "Пошаљи упит", + "@raiseTicket": { + "description": "Button text for raising a support tickets in case of unhandled errors during backup", + "type": "text" + }, + "backupFailed": "Неуспешна израда резервне копије", + "sorryBackupFailedDesc": "Извините, нисмо успели да копирамо овај фајл тренутно. Покушаћемо касније.", + "couldNotBackUpTryLater": "Нисмо могли да бекапујемо ваше податке.\nПокушаћемо касније.", + "enteCanEncryptAndPreserveFilesOnlyIfYouGrant": "Енте може да екриптује и сачува фајлове једино ако дозволите приступ њима", + "pleaseGrantPermissions": "Молимо, дозволите приступ", + "grantPermission": "Дозволите приступ", + "privateSharing": "Приватно дељење", + "shareOnlyWithThePeopleYouWant": "Дели само са људима са којима желиш", + "usePublicLinksForPeopleNotOnEnte": "Користи јавне линкове за људима који нису на Енте", + "allowPeopleToAddPhotos": "Дозволи људима да додају фотографије", + "shareAnAlbumNow": "Подели албум сада", + "collectEventPhotos": "Прикупи фотографије са догађаја", + "@onDevice": { + "description": "The text displayed above folders/albums stored on device", + "type": "text" + }, + "onDevice": "На уређају", + "@onEnte": { + "description": "The text displayed above albums backed up to Ente", + "type": "text" + }, + "onEnte": "У Енте", + "name": "Име", + "newest": "Најновије", + "lastUpdated": "Последње ажурирано", "deleteEmptyAlbums": "Избриши празне албуме", + "deleteEmptyAlbumsWithQuestionMark": "Обриши празне албуме?", + "permanentlyDelete": "Трајно избриши", + "canOnlyCreateLinkForFilesOwnedByYou": "Можете направити линк само за фајлове који су ваше власништво", + "linkCopiedToClipboard": "Линк је копиран у меморију", + "restore": "Поврати", + "@restore": { + "description": "Display text for an action which triggers a restore of item from trash", + "type": "text" + }, + "moveToAlbum": "Премести у албум", + "unarchive": "Врати из архиве", "createCollage": "Направи колаж", "saveCollage": "Сачувај колаж", "addToEnte": "Додај у Енте", @@ -475,6 +686,22 @@ "delete": "Обриши", "hide": "Сакриј", "share": "Подели", + "searchByAlbumNameHint": "Име албума", + "albumTitle": "Наслов албума", + "enterAlbumName": "Унесите име албума", + "movingFilesToAlbum": "Премештам фајлове у албум...", + "addedSuccessfullyTo": "Успешно додато у {albumName}", + "movedSuccessfullyTo": "Успешно премештено у {albumName}", + "thisAlbumAlreadyHDACollaborativeLink": "Овај албум већ има линк за сарадњу", + "collaborativeLinkCreatedFor": "Линк за сарадњу креиран за {albumName}", + "askYourLovedOnesToShare": "Замолите ваше вољене да деле", + "invite": "Позови", + "shareYourFirstAlbum": "Подели твој први албум", + "sharedWith": "Подељено са {emailIDs}", + "sharedWithMe": "Подељено са мном", + "sharedByMe": "Ја поделио", + "doubleYourStorage": "Удвостручи простор", + "referFriendsAnd2xYourPlan": "Препоручи пријатељима и удвостручи тренутни пакет", "rename": "Преименуј", "thisEmailIsAlreadyInUse": "Имејл је већ у употреби", "yourVerificationCodeHasExpired": "Ваш код ѕа верификацију је истекао", diff --git a/mobile/apps/photos/lib/l10n/intl_vi.arb b/mobile/apps/photos/lib/l10n/intl_vi.arb index d609d51994..6392a8957d 100644 --- a/mobile/apps/photos/lib/l10n/intl_vi.arb +++ b/mobile/apps/photos/lib/l10n/intl_vi.arb @@ -241,7 +241,7 @@ "linkHasExpired": "Liên kết đã hết hạn", "publicLinkEnabled": "Liên kết công khai đã được bật", "shareALink": "Chia sẻ một liên kết", - "sharedAlbumSectionDescription": "Tạo album chia sẻ và cộng tác với người dùng Ente khác, bao gồm cả người dùng các gói miễn phí.", + "sharedAlbumSectionDescription": "Tạo album chia sẻ và cộng tác với người dùng Ente khác, bao gồm người dùng gói miễn phí.", "shareWithPeopleSectionTitle": "{numberOfPeople, plural, =0 {Chia sẻ với những người cụ thể} =1 {Chia sẻ với 1 người} other {Chia sẻ với {numberOfPeople} người}}", "@shareWithPeopleSectionTitle": { "placeholders": { @@ -293,7 +293,7 @@ "theyAlsoGetXGb": "Họ cũng nhận được {storageAmountInGB} GB", "freeStorageOnReferralSuccess": "{storageAmountInGB} GB mỗi khi ai đó đăng ký gói trả phí và áp dụng mã của bạn", "shareTextReferralCode": "Mã giới thiệu Ente: {referralCode} \n\nÁp dụng nó trong Cài đặt → Chung → Giới thiệu để nhận thêm {referralStorageInGB} GB miễn phí sau khi bạn đăng ký gói trả phí\n\nhttps://ente.io", - "claimFreeStorage": "Nhận thêm dung lượng miễn phí", + "claimFreeStorage": "Nhận thêm dung lượng", "inviteYourFriends": "Mời bạn bè của bạn", "failedToFetchReferralDetails": "Không thể lấy thông tin giới thiệu. Vui lòng thử lại sau.", "referralStep1": "1. Đưa mã này cho bạn bè của bạn", diff --git a/mobile/apps/photos/lib/l10n/intl_zh.arb b/mobile/apps/photos/lib/l10n/intl_zh.arb index bb6219ced3..5ddb76d98f 100644 --- a/mobile/apps/photos/lib/l10n/intl_zh.arb +++ b/mobile/apps/photos/lib/l10n/intl_zh.arb @@ -1776,7 +1776,56 @@ "same": "相同", "different": "不同", "sameperson": "是同一个人?", + "cLTitle1": "高级图像编辑器", + "cLDesc1": "我们正在发布一款全新且高级的图像编辑器,新增更多裁剪框架、快速编辑的滤镜预设,以及包括饱和度、对比度、亮度、色温等在内的精细调整选项。新的编辑器还支持在照片上绘制和添加表情符号作为贴纸。", + "cLTitle2": "智能相册", + "cLDesc2": "您现在可以将所选人物的照片自动添加到任何相册。只需进入相册,从溢出菜单中选择“自动添加人物”。如果与共享相册一起使用,您可以零点击分享照片。", + "cLTitle3": "改进的相册", + "cLDesc3": "我们新增了按周、月、年对图库进行分组的功能。您现在可以通过这些新的分组选项以及自定义网格,定制图库的外观,完全按照您的喜好进行设置", + "cLTitle4": "更快滚动", + "cLDesc4": "除了多项后台改进以提升图库滚动体验外,我们还重新设计了滚动条,添加了标记功能,让您可以快速跳转到时间轴上的不同位置。", "indexingPausedStatusDescription": "索引已暂停。待设备准备就绪后,索引将自动恢复。当设备的电池电量、电池健康度和温度状态处于健康范围内时,设备即被视为准备就绪。", + "thisWeek": "本周", + "lastWeek": "上周", + "thisMonth": "本月", + "thisYear": "今年", + "groupBy": "分组依据", "faceThumbnailGenerationFailed": "无法生成人脸缩略图", - "fileAnalysisFailed": "无法分析文件" + "fileAnalysisFailed": "无法分析文件", + "editAutoAddPeople": "编辑自动添加人物", + "autoAddPeople": "自动添加人物", + "autoAddToAlbum": "自动添加到相册", + "shouldRemoveFilesSmartAlbumsDesc": "应该移除之前在智能相册中选择的与该人相关的文件吗?", + "addingPhotos": "正在添加照片", + "gettingReady": "准备就绪", + "addSomePhotosDesc1": "添加一些照片或者选择 ", + "addSomePhotosDesc2": "熟悉的面孔", + "addSomePhotosDesc3": "首先", + "ignorePerson": "忽略此人", + "mixedGrouping": "混合分组?", + "analysis": "分析", + "doesGroupContainMultiplePeople": "这个分组包含多个人吗?", + "automaticallyAnalyzeAndSplitGrouping": "我们将自动分析分组以确定是否有多人在场,并再次将他们分开。这可能需要几秒钟。", + "layout": "布局", + "day": "日", + "peopleAutoAddDesc": "选择您想要自动添加到相册的人", + "undo": "撤销", + "redo": "重做", + "filter": "筛选", + "adjust": "调整", + "draw": "绘制", + "sticker": "贴纸", + "brushColor": "笔刷颜色", + "font": "字体", + "background": "背景", + "align": "对齐", + "addedToAlbums": "{count, plural, =1{已成功添加到 1 个相册} other{已成功添加到 {count} 个相册}}", + "@addedToAlbums": { + "description": "Message shown when items are added to albums", + "placeholders": { + "count": { + "type": "int" + } + } + } } \ No newline at end of file From f2049ac7faf4c585126ce2e615fa91c1caad5732 Mon Sep 17 00:00:00 2001 From: Crowdin Bot Date: Mon, 18 Aug 2025 01:18:04 +0000 Subject: [PATCH 149/164] New Crowdin translations by GitHub Action --- mobile/apps/auth/lib/l10n/arb/app_el.arb | 28 +++++++++++++++++++++++- mobile/apps/auth/lib/l10n/arb/app_pl.arb | 2 +- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/mobile/apps/auth/lib/l10n/arb/app_el.arb b/mobile/apps/auth/lib/l10n/arb/app_el.arb index 8027a2fbc2..2a2a1d803a 100644 --- a/mobile/apps/auth/lib/l10n/arb/app_el.arb +++ b/mobile/apps/auth/lib/l10n/arb/app_el.arb @@ -88,6 +88,8 @@ "useRecoveryKey": "Χρήση κλειδιού ανάκτησης", "incorrectPasswordTitle": "Λάθος κωδικός πρόσβασης", "welcomeBack": "Καλωσορίσατε και πάλι!", + "emailAlreadyRegistered": "Το email είναι ήδη καταχωρημένο.", + "emailNotRegistered": "Το email δεν έχει καταχωρηθεί.", "madeWithLoveAtPrefix": "φτιαγμένη με ❤️ στο ", "supportDevs": "Εγγραφείτε στο ente για να μας υποστηρίξετε", "supportDiscount": "Χρησιμοποιήστε τον κωδικό κουπονιού \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο", @@ -171,6 +173,7 @@ "invalidQRCode": "Μη έγκυρος κωδικός QR", "noRecoveryKeyTitle": "Χωρίς κλειδί ανάκτησης;", "enterEmailHint": "Εισάγετε τη διεύθυνση email σας", + "enterNewEmailHint": "Εισάγετε την διεύθυνση ηλ. ταχυδρομείου σας", "invalidEmailTitle": "Μη έγκυρη διεύθυνση email", "invalidEmailMessage": "Παρακαλούμε εισάγετε μια έγκυρη διεύθυνση email.", "deleteAccount": "Διαγραφή λογαριασμού", @@ -258,6 +261,10 @@ "areYouSureYouWantToLogout": "Είστε σίγουροι ότι θέλετε να αποσυνδεθείτε;", "yesLogout": "Ναι, αποσύνδεση", "exit": "Εξοδος", + "theme": "Θέμα", + "lightTheme": "Φωτεινό", + "darkTheme": "Σκοτεινό", + "systemTheme": "Σύστημα", "verifyingRecoveryKey": "Επαλήθευση κλειδιού ανάκτησης...", "recoveryKeyVerified": "Το κλειδί ανάκτησης επαληθεύτηκε", "recoveryKeySuccessBody": "Τέλεια! Το κλειδί ανάκτησης σας είναι έγκυρο. Σας ευχαριστούμε για την επαλήθευση.\n\nΠαρακαλώ θυμηθείτε να κρατήσετε το κλειδί ανάκτησης σας και σε αντίγραφο ασφαλείας.", @@ -490,5 +497,24 @@ "appLockNotEnabled": "Το κλείδωμα εφαρμογής δεν είναι ενεργοποιημένο", "appLockNotEnabledDescription": "Παρακαλώ ενεργοποιήστε το κλείδωμα εφαρμογής μέσω της επιλογής Ασφάλεια > Κλείδωμα εφαρμογής", "authToViewPasskey": "Παρακαλώ πιστοποιηθείτε για να δείτε το κλειδί πρόσβασης", - "appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας." + "appLockOfflineModeWarning": "Έχετε επιλέξει να προχωρήσετε χωρίς αντίγραφα ασφαλείας. Αν ξεχάσετε τον κωδικό της εφαρμογής, θα κλειδωθείτε από την πρόσβαση στα δεδομένα σας.", + "duplicateCodes": "Διπλότυποι κωδικοί", + "noDuplicates": "✨ Δεν υπάρχουν διπλότυπα", + "youveNoDuplicateCodesThatCanBeCleared": "Δεν υπάρχουν διπλότυπα αρχεία που μπορούν να εκκαθαριστούν", + "deduplicateCodes": "Διπλότυποι κωδικοί", + "deselectAll": "Αποεπιλογή όλων", + "selectAll": "Επιλογή όλων", + "deleteDuplicates": "Διαγραφή διπλότυπων", + "plainHTML": "Απλό HTML", + "dropReviewiOS": "Αφήστε μια κριτική στο App Store", + "dropReviewAndroid": "Αφήστε μια κριτική στο Play Store", + "giveUsAStarOnGithub": "Δώστε μας ένα αστέρι στο Github", + "free5GB": "5GB δωρεάν στο ente Photos", + "freeStorageOffer": "10% έκπτωση στο ente photos", + "freeStorageOfferDescription": "Χρησιμοποιήστε τον κωδικό \"AUTH\" για να λάβετε 10% έκπτωση για τον πρώτο χρόνο", + "advanced": "Για προχωρημένους", + "algorithm": "Αλγόριθμος", + "type": "Τύπος", + "period": "Περίοδος", + "digits": "Ψηφία" } \ No newline at end of file diff --git a/mobile/apps/auth/lib/l10n/arb/app_pl.arb b/mobile/apps/auth/lib/l10n/arb/app_pl.arb index f17467db12..a4177c8133 100644 --- a/mobile/apps/auth/lib/l10n/arb/app_pl.arb +++ b/mobile/apps/auth/lib/l10n/arb/app_pl.arb @@ -45,7 +45,7 @@ "timeBasedKeyType": "Oparte na czasie (TOTP)", "counterBasedKeyType": "Oparte na liczniku (HOTP)", "saveAction": "Zapisz", - "nextTotpTitle": "następny", + "nextTotpTitle": "dalej", "deleteCodeTitle": "Usunąć kod?", "deleteCodeMessage": "Czy na pewno chcesz usunąć ten kod? Ta akcja jest nieodwracalna.", "trashCode": "Przenieść kod do kosza?", From 435ed212c6cf86d39168b30f5e0c0c846fd5c01d Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Mon, 18 Aug 2025 12:13:15 +0530 Subject: [PATCH 150/164] Fix build issues --- mobile/apps/auth/android/app/build.gradle | 2 +- mobile/apps/auth/lib/ui/scanner_gauth_page.dart | 2 +- mobile/apps/auth/lib/ui/scanner_page.dart | 2 +- mobile/apps/auth/pubspec.lock | 2 +- mobile/packages/utils/lib/platform_util.dart | 4 ++-- mobile/packages/utils/pubspec.lock | 4 ++-- mobile/packages/utils/pubspec.yaml | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/mobile/apps/auth/android/app/build.gradle b/mobile/apps/auth/android/app/build.gradle index 4633c502f3..15d144c0c4 100644 --- a/mobile/apps/auth/android/app/build.gradle +++ b/mobile/apps/auth/android/app/build.gradle @@ -59,7 +59,7 @@ android { applicationId "io.ente.auth" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion 21 + minSdkVersion 22 targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName diff --git a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart index 58e6da3b41..41d0a762dd 100644 --- a/mobile/apps/auth/lib/ui/scanner_gauth_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_gauth_page.dart @@ -6,7 +6,7 @@ import 'package:ente_auth/theme/ente_theme.dart'; import 'package:ente_auth/ui/settings/data/import/google_auth_import.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; class ScannerGoogleAuthPage extends StatefulWidget { const ScannerGoogleAuthPage({super.key}); diff --git a/mobile/apps/auth/lib/ui/scanner_page.dart b/mobile/apps/auth/lib/ui/scanner_page.dart index 65eabf6d3a..877920390e 100644 --- a/mobile/apps/auth/lib/ui/scanner_page.dart +++ b/mobile/apps/auth/lib/ui/scanner_page.dart @@ -4,7 +4,7 @@ import 'package:ente_auth/l10n/l10n.dart'; import 'package:ente_auth/models/code.dart'; import 'package:ente_auth/utils/toast_util.dart'; import 'package:flutter/material.dart'; -import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart'; +import 'package:qr_code_scanner/qr_code_scanner.dart'; class ScannerPage extends StatefulWidget { const ScannerPage({super.key}); diff --git a/mobile/apps/auth/pubspec.lock b/mobile/apps/auth/pubspec.lock index 3c4373c96e..8a8b4fd6b1 100644 --- a/mobile/apps/auth/pubspec.lock +++ b/mobile/apps/auth/pubspec.lock @@ -1391,7 +1391,7 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" - qr_code_scanner_plus: + qr_code_scanner: dependency: "direct main" description: path: "." diff --git a/mobile/packages/utils/lib/platform_util.dart b/mobile/packages/utils/lib/platform_util.dart index e84349c301..6ad91b0d88 100644 --- a/mobile/packages/utils/lib/platform_util.dart +++ b/mobile/packages/utils/lib/platform_util.dart @@ -43,14 +43,14 @@ class PlatformUtil { if (Platform.isAndroid || Platform.isIOS) { await FileSaver.instance.saveAs( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); } else { await FileSaver.instance.saveFile( name: fileName, - ext: extension, + fileExtension: extension, bytes: bytes, mimeType: type, ); diff --git a/mobile/packages/utils/pubspec.lock b/mobile/packages/utils/pubspec.lock index c5ec3f8a8a..154ea69750 100644 --- a/mobile/packages/utils/pubspec.lock +++ b/mobile/packages/utils/pubspec.lock @@ -232,10 +232,10 @@ packages: dependency: "direct main" description: name: file_saver - sha256: "448b1e30142cffe52f37ee085ea9ca50670d5425bb09b649d193549b2dcf6e26" + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "0.3.1" fixnum: dependency: transitive description: diff --git a/mobile/packages/utils/pubspec.yaml b/mobile/packages/utils/pubspec.yaml index 42af64e151..e208662108 100644 --- a/mobile/packages/utils/pubspec.yaml +++ b/mobile/packages/utils/pubspec.yaml @@ -18,7 +18,7 @@ dependencies: path: ../../packages/strings ente_ui: path: ../../packages/ui - file_saver: 0.3.0 + file_saver: ^0.3.1 flutter: sdk: flutter flutter_email_sender: ^7.0.0 From df584f34e94bfbf0a4a6b4646b4924f678f64e69 Mon Sep 17 00:00:00 2001 From: AmanRajSinghMourya Date: Mon, 18 Aug 2025 12:36:05 +0530 Subject: [PATCH 151/164] Remove unused script for dependency fetching in mobile packages --- mobile/packages/script.sh | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100755 mobile/packages/script.sh diff --git a/mobile/packages/script.sh b/mobile/packages/script.sh deleted file mode 100755 index 5ae3f45ec6..0000000000 --- a/mobile/packages/script.sh +++ /dev/null @@ -1,13 +0,0 @@ -// filepath: /Users/amanraj/development/ente/mobile/packages/script.sh -#!/bin/bash - -# Exit immediately if a command exits with a non-zero status. -set -e - -echo "🚀 Starting dependency fetch for all Flutter/Dart projects..." - -# Find all directories containing a pubspec.yaml and run 'flutter pub get' in them. -# This covers 'apps' and 'packages' directories. -find . -name "pubspec.yaml" -execdir flutter pub get \; - -echo "✅ All dependencies fetched successfully!" \ No newline at end of file From 777516446d789b7802d9fff56633d4c854fb63a6 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Mon, 18 Aug 2025 18:43:14 +0530 Subject: [PATCH 152/164] Hey Locker! --- mobile/apps/locker/.gitignore | 54 + mobile/apps/locker/README.md | 1 + mobile/apps/locker/analysis_options.yaml | 72 + mobile/apps/locker/android/.gitignore | 13 + mobile/apps/locker/android/app/build.gradle | 44 + .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 57 + .../kotlin/io/ente/locker/MainActivity.kt | 5 + .../res/drawable-v21/launch_background.xml | 12 + .../main/res/drawable/launch_background.xml | 12 + .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../app/src/main/res/values/styles.xml | 18 + .../app/src/profile/AndroidManifest.xml | 7 + mobile/apps/locker/android/build.gradle | 18 + mobile/apps/locker/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + mobile/apps/locker/android/settings.gradle | 25 + .../apps/locker/assets/fonts/Inter-Bold.ttf | Bin 0 -> 715204 bytes .../apps/locker/assets/fonts/Inter-Light.ttf | Bin 0 -> 675196 bytes .../apps/locker/assets/fonts/Inter-Medium.ttf | Bin 0 -> 694512 bytes .../locker/assets/fonts/Inter-Regular.ttf | Bin 0 -> 680240 bytes .../locker/assets/fonts/Inter-SemiBold.ttf | Bin 0 -> 710040 bytes .../locker/assets/fonts/Montserrat-Bold.ttf | Bin 0 -> 198612 bytes mobile/apps/locker/assets/locker.png | Bin 0 -> 109405 bytes mobile/apps/locker/ios/.gitignore | 34 + .../locker/ios/Flutter/AppFrameworkInfo.plist | 26 + mobile/apps/locker/ios/Flutter/Debug.xcconfig | 2 + .../apps/locker/ios/Flutter/Release.xcconfig | 2 + mobile/apps/locker/ios/Podfile | 59 + mobile/apps/locker/ios/Podfile.lock | 225 +++ .../ios/Runner.xcodeproj/project.pbxproj | 1027 ++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 101 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../apps/locker/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++ .../Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/README.md | 5 + .../Runner/Base.lproj/LaunchScreen.storyboard | 37 + .../ios/Runner/Base.lproj/Main.storyboard | 26 + mobile/apps/locker/ios/Runner/Info.plist | 68 + .../ios/Runner/Runner-Bridging-Header.h | 1 + .../locker/ios/Runner/Runner.entitlements | 10 + .../locker/ios/RunnerTests/RunnerTests.swift | 12 + .../Base.lproj/MainInterface.storyboard | 24 + .../locker/ios/Share Extension/Info.plist | 33 + .../Share Extension.entitlements | 10 + .../Share Extension/ShareViewController.swift | 10 + mobile/apps/locker/l10n.yaml | 3 + mobile/apps/locker/lib/app.dart | 220 +++ mobile/apps/locker/lib/core/constants.dart | 45 + mobile/apps/locker/lib/core/errors.dart | 41 + mobile/apps/locker/lib/core/locale.dart | 74 + .../lib/events/backup_updated_event.dart | 10 + .../lib/events/collections_updated_event.dart | 3 + mobile/apps/locker/lib/l10n/app_en.arb | 312 ++++ .../locker/lib/l10n/app_localizations.dart | 805 +++++++++ .../locker/lib/l10n/app_localizations_en.dart | 410 +++++ mobile/apps/locker/lib/l10n/l10n.dart | 8 + mobile/apps/locker/lib/main.dart | 163 ++ .../collections/collections_api_client.dart | 452 +++++ .../services/collections/collections_db.dart | 535 ++++++ .../collections/collections_service.dart | 343 ++++ .../collections/models/collection.dart | 352 ++++ .../models/collection_file_item.dart | 66 + .../collections/models/collection_magic.dart | 126 ++ .../lib/services/collections/models/diff.dart | 15 + .../collections/models/public_url.dart | 63 + .../lib/services/collections/models/user.dart | 44 + .../locker/lib/services/configuration.dart | 6 + .../files/download/file_downloader.dart | 115 ++ .../lib/services/files/download/file_url.dart | 41 + .../lib/services/files/download/manager.dart | 373 +++++ .../services/files/download/models/task.dart | 80 + .../files/download/service_locator.dart | 36 + .../services/files/links/links_client.dart | 39 + .../services/files/links/links_service.dart | 29 + .../files/links/models/shareable_link.dart | 148 ++ .../files/sync/metadata_updater_service.dart | 202 +++ .../files/sync/models/common_keys.dart | 8 + .../lib/services/files/sync/models/file.dart | 123 ++ .../files/sync/models/file_magic.dart | 127 ++ .../files/sync/models/metadata_request.dart | 29 + .../files/upload/file_upload_service.dart | 684 ++++++++ .../files/upload/models/backup_item.dart | 62 + .../upload/models/backup_item_status.dart | 7 + .../files/upload/models/upload_url.dart | 26 + .../lib/services/trash/models/trash_file.dart | 14 + .../trash/models/trash_item_request.dart | 22 + .../locker/lib/services/trash/trash_db.dart | 173 ++ .../lib/services/trash/trash_service.dart | 279 ++++ .../collection_selection_widget.dart | 250 +++ .../lib/ui/components/file_edit_dialog.dart | 234 +++ .../lib/ui/components/file_upload_dialog.dart | 208 +++ .../information_addition_dialog.dart | 208 +++ .../lib/ui/components/item_list_view.dart | 1296 +++++++++++++++ .../ui/components/recents_section_widget.dart | 462 ++++++ .../lib/ui/components/search_result_view.dart | 60 + .../locker/lib/ui/mixins/search_mixin.dart | 348 ++++ .../lib/ui/pages/all_collections_page.dart | 408 +++++ .../locker/lib/ui/pages/collection_page.dart | 275 +++ .../apps/locker/lib/ui/pages/home_page.dart | 785 +++++++++ .../locker/lib/ui/pages/onboarding_page.dart | 227 +++ .../apps/locker/lib/ui/pages/trash_page.dart | 404 +++++ .../locker/lib/ui/pages/uploader_page.dart | 111 ++ .../locker/lib/utils/collection_actions.dart | 160 ++ .../lib/utils/collection_sort_util.dart | 101 ++ .../apps/locker/lib/utils/crypto_helper.dart | 48 + mobile/apps/locker/lib/utils/data_util.dart | 63 + .../apps/locker/lib/utils/date_time_util.dart | 259 +++ .../locker/lib/utils/file_icon_utils.dart | 72 + .../locker/lib/utils/snack_bar_utils.dart | 36 + mobile/apps/locker/linux/.gitignore | 1 + mobile/apps/locker/linux/CMakeLists.txt | 145 ++ .../apps/locker/linux/flutter/CMakeLists.txt | 88 + .../flutter/generated_plugin_registrant.cc | 55 + .../flutter/generated_plugin_registrant.h | 15 + .../linux/flutter/generated_plugins.cmake | 35 + mobile/apps/locker/linux/main.cc | 6 + mobile/apps/locker/linux/my_application.cc | 124 ++ mobile/apps/locker/linux/my_application.h | 18 + mobile/apps/locker/macos/.gitignore | 7 + .../macos/Flutter/Flutter-Debug.xcconfig | 2 + .../macos/Flutter/Flutter-Release.xcconfig | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 50 + mobile/apps/locker/macos/Podfile | 43 + mobile/apps/locker/macos/Podfile.lock | 152 ++ .../macos/Runner.xcodeproj/project.pbxproj | 809 +++++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 98 ++ .../contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../locker/macos/Runner/AppDelegate.swift | 9 + .../AppIcon.appiconset/Contents.json | 68 + .../AppIcon.appiconset/app_icon_1024.png | Bin 0 -> 102994 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 0 -> 5680 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 0 -> 520 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 0 -> 14142 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 0 -> 1066 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 0 -> 36406 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 0 -> 2218 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 343 ++++ .../macos/Runner/Configs/AppInfo.xcconfig | 17 + .../macos/Runner/Configs/Debug.xcconfig | 2 + .../macos/Runner/Configs/Release.xcconfig | 2 + .../macos/Runner/Configs/Warnings.xcconfig | 13 + .../macos/Runner/DebugProfile.entitlements | 18 + mobile/apps/locker/macos/Runner/Info.plist | 32 + .../macos/Runner/MainFlutterWindow.swift | 15 + .../locker/macos/Runner/Release.entitlements | 14 + .../macos/RunnerTests/RunnerTests.swift | 12 + mobile/apps/locker/pubspec.lock | 1471 +++++++++++++++++ mobile/apps/locker/pubspec.yaml | 87 + mobile/apps/locker/web/favicon.png | Bin 0 -> 917 bytes mobile/apps/locker/web/icons/Icon-192.png | Bin 0 -> 5292 bytes mobile/apps/locker/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../locker/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../locker/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes mobile/apps/locker/web/index.html | 38 + mobile/apps/locker/web/manifest.json | 35 + mobile/apps/locker/windows/.gitignore | 17 + mobile/apps/locker/windows/CMakeLists.txt | 108 ++ .../locker/windows/flutter/CMakeLists.txt | 109 ++ .../flutter/generated_plugin_registrant.cc | 50 + .../flutter/generated_plugin_registrant.h | 15 + .../windows/flutter/generated_plugins.cmake | 37 + .../apps/locker/windows/runner/CMakeLists.txt | 40 + mobile/apps/locker/windows/runner/Runner.rc | 121 ++ .../locker/windows/runner/flutter_window.cpp | 71 + .../locker/windows/runner/flutter_window.h | 33 + mobile/apps/locker/windows/runner/main.cpp | 43 + mobile/apps/locker/windows/runner/resource.h | 16 + .../windows/runner/resources/app_icon.ico | Bin 0 -> 33772 bytes .../locker/windows/runner/runner.exe.manifest | 14 + mobile/apps/locker/windows/runner/utils.cpp | 65 + mobile/apps/locker/windows/runner/utils.h | 19 + .../locker/windows/runner/win32_window.cpp | 288 ++++ .../apps/locker/windows/runner/win32_window.h | 102 ++ 205 files changed, 20018 insertions(+) create mode 100644 mobile/apps/locker/.gitignore create mode 100644 mobile/apps/locker/README.md create mode 100644 mobile/apps/locker/analysis_options.yaml create mode 100644 mobile/apps/locker/android/.gitignore create mode 100644 mobile/apps/locker/android/app/build.gradle create mode 100644 mobile/apps/locker/android/app/src/debug/AndroidManifest.xml create mode 100644 mobile/apps/locker/android/app/src/main/AndroidManifest.xml create mode 100644 mobile/apps/locker/android/app/src/main/kotlin/io/ente/locker/MainActivity.kt create mode 100644 mobile/apps/locker/android/app/src/main/res/drawable-v21/launch_background.xml create mode 100644 mobile/apps/locker/android/app/src/main/res/drawable/launch_background.xml create mode 100644 mobile/apps/locker/android/app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 mobile/apps/locker/android/app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 mobile/apps/locker/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 mobile/apps/locker/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 mobile/apps/locker/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 mobile/apps/locker/android/app/src/main/res/values-night/styles.xml create mode 100644 mobile/apps/locker/android/app/src/main/res/values/styles.xml create mode 100644 mobile/apps/locker/android/app/src/profile/AndroidManifest.xml create mode 100644 mobile/apps/locker/android/build.gradle create mode 100644 mobile/apps/locker/android/gradle.properties create mode 100644 mobile/apps/locker/android/gradle/wrapper/gradle-wrapper.properties create mode 100644 mobile/apps/locker/android/settings.gradle create mode 100644 mobile/apps/locker/assets/fonts/Inter-Bold.ttf create mode 100644 mobile/apps/locker/assets/fonts/Inter-Light.ttf create mode 100644 mobile/apps/locker/assets/fonts/Inter-Medium.ttf create mode 100644 mobile/apps/locker/assets/fonts/Inter-Regular.ttf create mode 100644 mobile/apps/locker/assets/fonts/Inter-SemiBold.ttf create mode 100644 mobile/apps/locker/assets/fonts/Montserrat-Bold.ttf create mode 100644 mobile/apps/locker/assets/locker.png create mode 100644 mobile/apps/locker/ios/.gitignore create mode 100644 mobile/apps/locker/ios/Flutter/AppFrameworkInfo.plist create mode 100644 mobile/apps/locker/ios/Flutter/Debug.xcconfig create mode 100644 mobile/apps/locker/ios/Flutter/Release.xcconfig create mode 100644 mobile/apps/locker/ios/Podfile create mode 100644 mobile/apps/locker/ios/Podfile.lock create mode 100644 mobile/apps/locker/ios/Runner.xcodeproj/project.pbxproj create mode 100644 mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 mobile/apps/locker/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 mobile/apps/locker/ios/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 mobile/apps/locker/ios/Runner/AppDelegate.swift create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png create mode 100644 mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md create mode 100644 mobile/apps/locker/ios/Runner/Base.lproj/LaunchScreen.storyboard create mode 100644 mobile/apps/locker/ios/Runner/Base.lproj/Main.storyboard create mode 100644 mobile/apps/locker/ios/Runner/Info.plist create mode 100644 mobile/apps/locker/ios/Runner/Runner-Bridging-Header.h create mode 100644 mobile/apps/locker/ios/Runner/Runner.entitlements create mode 100644 mobile/apps/locker/ios/RunnerTests/RunnerTests.swift create mode 100644 mobile/apps/locker/ios/Share Extension/Base.lproj/MainInterface.storyboard create mode 100644 mobile/apps/locker/ios/Share Extension/Info.plist create mode 100644 mobile/apps/locker/ios/Share Extension/Share Extension.entitlements create mode 100644 mobile/apps/locker/ios/Share Extension/ShareViewController.swift create mode 100644 mobile/apps/locker/l10n.yaml create mode 100644 mobile/apps/locker/lib/app.dart create mode 100644 mobile/apps/locker/lib/core/constants.dart create mode 100644 mobile/apps/locker/lib/core/errors.dart create mode 100644 mobile/apps/locker/lib/core/locale.dart create mode 100644 mobile/apps/locker/lib/events/backup_updated_event.dart create mode 100644 mobile/apps/locker/lib/events/collections_updated_event.dart create mode 100644 mobile/apps/locker/lib/l10n/app_en.arb create mode 100644 mobile/apps/locker/lib/l10n/app_localizations.dart create mode 100644 mobile/apps/locker/lib/l10n/app_localizations_en.dart create mode 100644 mobile/apps/locker/lib/l10n/l10n.dart create mode 100644 mobile/apps/locker/lib/main.dart create mode 100644 mobile/apps/locker/lib/services/collections/collections_api_client.dart create mode 100644 mobile/apps/locker/lib/services/collections/collections_db.dart create mode 100644 mobile/apps/locker/lib/services/collections/collections_service.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/collection.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/collection_file_item.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/collection_magic.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/diff.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/public_url.dart create mode 100644 mobile/apps/locker/lib/services/collections/models/user.dart create mode 100644 mobile/apps/locker/lib/services/configuration.dart create mode 100644 mobile/apps/locker/lib/services/files/download/file_downloader.dart create mode 100644 mobile/apps/locker/lib/services/files/download/file_url.dart create mode 100644 mobile/apps/locker/lib/services/files/download/manager.dart create mode 100644 mobile/apps/locker/lib/services/files/download/models/task.dart create mode 100644 mobile/apps/locker/lib/services/files/download/service_locator.dart create mode 100644 mobile/apps/locker/lib/services/files/links/links_client.dart create mode 100644 mobile/apps/locker/lib/services/files/links/links_service.dart create mode 100644 mobile/apps/locker/lib/services/files/links/models/shareable_link.dart create mode 100644 mobile/apps/locker/lib/services/files/sync/metadata_updater_service.dart create mode 100644 mobile/apps/locker/lib/services/files/sync/models/common_keys.dart create mode 100644 mobile/apps/locker/lib/services/files/sync/models/file.dart create mode 100644 mobile/apps/locker/lib/services/files/sync/models/file_magic.dart create mode 100644 mobile/apps/locker/lib/services/files/sync/models/metadata_request.dart create mode 100644 mobile/apps/locker/lib/services/files/upload/file_upload_service.dart create mode 100644 mobile/apps/locker/lib/services/files/upload/models/backup_item.dart create mode 100644 mobile/apps/locker/lib/services/files/upload/models/backup_item_status.dart create mode 100644 mobile/apps/locker/lib/services/files/upload/models/upload_url.dart create mode 100644 mobile/apps/locker/lib/services/trash/models/trash_file.dart create mode 100644 mobile/apps/locker/lib/services/trash/models/trash_item_request.dart create mode 100644 mobile/apps/locker/lib/services/trash/trash_db.dart create mode 100644 mobile/apps/locker/lib/services/trash/trash_service.dart create mode 100644 mobile/apps/locker/lib/ui/components/collection_selection_widget.dart create mode 100644 mobile/apps/locker/lib/ui/components/file_edit_dialog.dart create mode 100644 mobile/apps/locker/lib/ui/components/file_upload_dialog.dart create mode 100644 mobile/apps/locker/lib/ui/components/information_addition_dialog.dart create mode 100644 mobile/apps/locker/lib/ui/components/item_list_view.dart create mode 100644 mobile/apps/locker/lib/ui/components/recents_section_widget.dart create mode 100644 mobile/apps/locker/lib/ui/components/search_result_view.dart create mode 100644 mobile/apps/locker/lib/ui/mixins/search_mixin.dart create mode 100644 mobile/apps/locker/lib/ui/pages/all_collections_page.dart create mode 100644 mobile/apps/locker/lib/ui/pages/collection_page.dart create mode 100644 mobile/apps/locker/lib/ui/pages/home_page.dart create mode 100644 mobile/apps/locker/lib/ui/pages/onboarding_page.dart create mode 100644 mobile/apps/locker/lib/ui/pages/trash_page.dart create mode 100644 mobile/apps/locker/lib/ui/pages/uploader_page.dart create mode 100644 mobile/apps/locker/lib/utils/collection_actions.dart create mode 100644 mobile/apps/locker/lib/utils/collection_sort_util.dart create mode 100644 mobile/apps/locker/lib/utils/crypto_helper.dart create mode 100644 mobile/apps/locker/lib/utils/data_util.dart create mode 100644 mobile/apps/locker/lib/utils/date_time_util.dart create mode 100644 mobile/apps/locker/lib/utils/file_icon_utils.dart create mode 100644 mobile/apps/locker/lib/utils/snack_bar_utils.dart create mode 100644 mobile/apps/locker/linux/.gitignore create mode 100644 mobile/apps/locker/linux/CMakeLists.txt create mode 100644 mobile/apps/locker/linux/flutter/CMakeLists.txt create mode 100644 mobile/apps/locker/linux/flutter/generated_plugin_registrant.cc create mode 100644 mobile/apps/locker/linux/flutter/generated_plugin_registrant.h create mode 100644 mobile/apps/locker/linux/flutter/generated_plugins.cmake create mode 100644 mobile/apps/locker/linux/main.cc create mode 100644 mobile/apps/locker/linux/my_application.cc create mode 100644 mobile/apps/locker/linux/my_application.h create mode 100644 mobile/apps/locker/macos/.gitignore create mode 100644 mobile/apps/locker/macos/Flutter/Flutter-Debug.xcconfig create mode 100644 mobile/apps/locker/macos/Flutter/Flutter-Release.xcconfig create mode 100644 mobile/apps/locker/macos/Flutter/GeneratedPluginRegistrant.swift create mode 100644 mobile/apps/locker/macos/Podfile create mode 100644 mobile/apps/locker/macos/Podfile.lock create mode 100644 mobile/apps/locker/macos/Runner.xcodeproj/project.pbxproj create mode 100644 mobile/apps/locker/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/apps/locker/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme create mode 100644 mobile/apps/locker/macos/Runner.xcworkspace/contents.xcworkspacedata create mode 100644 mobile/apps/locker/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 mobile/apps/locker/macos/Runner/AppDelegate.swift create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png create mode 100644 mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png create mode 100644 mobile/apps/locker/macos/Runner/Base.lproj/MainMenu.xib create mode 100644 mobile/apps/locker/macos/Runner/Configs/AppInfo.xcconfig create mode 100644 mobile/apps/locker/macos/Runner/Configs/Debug.xcconfig create mode 100644 mobile/apps/locker/macos/Runner/Configs/Release.xcconfig create mode 100644 mobile/apps/locker/macos/Runner/Configs/Warnings.xcconfig create mode 100644 mobile/apps/locker/macos/Runner/DebugProfile.entitlements create mode 100644 mobile/apps/locker/macos/Runner/Info.plist create mode 100644 mobile/apps/locker/macos/Runner/MainFlutterWindow.swift create mode 100644 mobile/apps/locker/macos/Runner/Release.entitlements create mode 100644 mobile/apps/locker/macos/RunnerTests/RunnerTests.swift create mode 100644 mobile/apps/locker/pubspec.lock create mode 100644 mobile/apps/locker/pubspec.yaml create mode 100644 mobile/apps/locker/web/favicon.png create mode 100644 mobile/apps/locker/web/icons/Icon-192.png create mode 100644 mobile/apps/locker/web/icons/Icon-512.png create mode 100644 mobile/apps/locker/web/icons/Icon-maskable-192.png create mode 100644 mobile/apps/locker/web/icons/Icon-maskable-512.png create mode 100644 mobile/apps/locker/web/index.html create mode 100644 mobile/apps/locker/web/manifest.json create mode 100644 mobile/apps/locker/windows/.gitignore create mode 100644 mobile/apps/locker/windows/CMakeLists.txt create mode 100644 mobile/apps/locker/windows/flutter/CMakeLists.txt create mode 100644 mobile/apps/locker/windows/flutter/generated_plugin_registrant.cc create mode 100644 mobile/apps/locker/windows/flutter/generated_plugin_registrant.h create mode 100644 mobile/apps/locker/windows/flutter/generated_plugins.cmake create mode 100644 mobile/apps/locker/windows/runner/CMakeLists.txt create mode 100644 mobile/apps/locker/windows/runner/Runner.rc create mode 100644 mobile/apps/locker/windows/runner/flutter_window.cpp create mode 100644 mobile/apps/locker/windows/runner/flutter_window.h create mode 100644 mobile/apps/locker/windows/runner/main.cpp create mode 100644 mobile/apps/locker/windows/runner/resource.h create mode 100644 mobile/apps/locker/windows/runner/resources/app_icon.ico create mode 100644 mobile/apps/locker/windows/runner/runner.exe.manifest create mode 100644 mobile/apps/locker/windows/runner/utils.cpp create mode 100644 mobile/apps/locker/windows/runner/utils.h create mode 100644 mobile/apps/locker/windows/runner/win32_window.cpp create mode 100644 mobile/apps/locker/windows/runner/win32_window.h diff --git a/mobile/apps/locker/.gitignore b/mobile/apps/locker/.gitignore new file mode 100644 index 0000000000..9e1d55375a --- /dev/null +++ b/mobile/apps/locker/.gitignore @@ -0,0 +1,54 @@ +# Let folks use their custom editor settings +.vscode +.idea + +# macOS +.DS_Store + +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# Editors +.vscode/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# Flutter/Dart/Pub related +**/doc/api/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Web related +lib/generated_plugin_registrant.dart + +# Exceptions to above rules. +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages + +android/key.properties +android/app/.settings/* +android/.settings/ +.env + +fastlane/report.xml + +# Android related +android/app/build/ + +devtools_options.yaml diff --git a/mobile/apps/locker/README.md b/mobile/apps/locker/README.md new file mode 100644 index 0000000000..29c7d095bf --- /dev/null +++ b/mobile/apps/locker/README.md @@ -0,0 +1 @@ +# soon. diff --git a/mobile/apps/locker/analysis_options.yaml b/mobile/apps/locker/analysis_options.yaml new file mode 100644 index 0000000000..1bd78bc1b0 --- /dev/null +++ b/mobile/apps/locker/analysis_options.yaml @@ -0,0 +1,72 @@ +# For more linters, we can check https://dart-lang.github.io/linter/lints/index.html +# or https://pub.dev/packages/lint (Effective dart) +# use "flutter analyze ." or "dart analyze ." for running lint checks + +include: package:flutter_lints/flutter.yaml +linter: + rules: + # Ref https://github.com/flutter/packages/blob/master/packages/flutter_lints/lib/flutter.yaml + # Ref https://dart-lang.github.io/linter/lints/ + - avoid_print + - avoid_unnecessary_containers + - avoid_web_libraries_in_flutter + - no_logic_in_create_state + - prefer_const_constructors + - prefer_const_constructors_in_immutables + - prefer_const_declarations + - prefer_const_literals_to_create_immutables + - prefer_final_locals + - require_trailing_commas + - sized_box_for_whitespace + - use_full_hex_values_for_flutter_colors + - use_key_in_widget_constructors + - cancel_subscriptions + + + - avoid_empty_else + - exhaustive_cases + + # just style suggestions + - sort_pub_dependencies + - use_rethrow_when_possible + - prefer_double_quotes + - directives_ordering + - always_use_package_imports + - sort_child_properties_last + - unawaited_futures + +analyzer: + errors: + avoid_empty_else: error + exhaustive_cases: error + curly_braces_in_flow_control_structures: error + directives_ordering: error + require_trailing_commas: error + always_use_package_imports: warning + prefer_final_fields: error + unused_import: error + camel_case_types: error + prefer_is_empty: warning + use_rethrow_when_possible: info + unused_field: warning + use_key_in_widget_constructors: warning + sort_child_properties_last: warning + sort_pub_dependencies: warning + library_private_types_in_public_api: warning + constant_identifier_names: ignore + prefer_const_constructors: warning + prefer_const_declarations: warning + prefer_const_constructors_in_immutables: warning + prefer_final_locals: warning + unnecessary_const: error + cancel_subscriptions: error + unrelated_type_equality_checks: error + unnecessary_cast: info + + + unawaited_futures: warning # convert to warning after fixing existing issues + invalid_dependency: info + use_build_context_synchronously: ignore # experimental lint, requires many changes + prefer_interpolation_to_compose_strings: ignore # later too many warnings + prefer_double_quotes: ignore # too many warnings + avoid_renaming_method_parameters: ignore # incorrect warnings for `equals` overrides diff --git a/mobile/apps/locker/android/.gitignore b/mobile/apps/locker/android/.gitignore new file mode 100644 index 0000000000..55afd919c6 --- /dev/null +++ b/mobile/apps/locker/android/.gitignore @@ -0,0 +1,13 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/mobile/apps/locker/android/app/build.gradle b/mobile/apps/locker/android/app/build.gradle new file mode 100644 index 0000000000..6aa7b5d563 --- /dev/null +++ b/mobile/apps/locker/android/app/build.gradle @@ -0,0 +1,44 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +android { + namespace = "io.ente.locker" + compileSdk = 35 + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "io.ente.locker" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = 26 + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/mobile/apps/locker/android/app/src/debug/AndroidManifest.xml b/mobile/apps/locker/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/mobile/apps/locker/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/mobile/apps/locker/android/app/src/main/AndroidManifest.xml b/mobile/apps/locker/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..97161a63ea --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/android/app/src/main/kotlin/io/ente/locker/MainActivity.kt b/mobile/apps/locker/android/app/src/main/kotlin/io/ente/locker/MainActivity.kt new file mode 100644 index 0000000000..8cf01b258d --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/kotlin/io/ente/locker/MainActivity.kt @@ -0,0 +1,5 @@ +package io.ente.locker + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/mobile/apps/locker/android/app/src/main/res/drawable-v21/launch_background.xml b/mobile/apps/locker/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000000..f74085f3f6 --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/apps/locker/android/app/src/main/res/drawable/launch_background.xml b/mobile/apps/locker/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000000..304732f884 --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/mobile/apps/locker/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/mobile/apps/locker/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..db77bb4b7b0906d62b1847e87f15cdcacf6a4f29 GIT binary patch literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/mobile/apps/locker/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..17987b79bb8a35cc66c3c1fd44f5a5526c1b78be GIT binary patch literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/mobile/apps/locker/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..d5f1c8d34e7a88e3f88bea192c3a370d44689c3c GIT binary patch literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/mobile/apps/locker/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..4d6372eebdb28e45604e46eeda8dd24651419bc0 GIT binary patch literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/android/app/src/main/res/values-night/styles.xml b/mobile/apps/locker/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000000..06952be745 --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/apps/locker/android/app/src/main/res/values/styles.xml b/mobile/apps/locker/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..cb1ef88056 --- /dev/null +++ b/mobile/apps/locker/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/mobile/apps/locker/android/app/src/profile/AndroidManifest.xml b/mobile/apps/locker/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000000..399f6981d5 --- /dev/null +++ b/mobile/apps/locker/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/mobile/apps/locker/android/build.gradle b/mobile/apps/locker/android/build.gradle new file mode 100644 index 0000000000..d2ffbffa4c --- /dev/null +++ b/mobile/apps/locker/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/mobile/apps/locker/android/gradle.properties b/mobile/apps/locker/android/gradle.properties new file mode 100644 index 0000000000..2597170821 --- /dev/null +++ b/mobile/apps/locker/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/mobile/apps/locker/android/gradle/wrapper/gradle-wrapper.properties b/mobile/apps/locker/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..7bb2df6ba6 --- /dev/null +++ b/mobile/apps/locker/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip diff --git a/mobile/apps/locker/android/settings.gradle b/mobile/apps/locker/android/settings.gradle new file mode 100644 index 0000000000..b9e43bd376 --- /dev/null +++ b/mobile/apps/locker/android/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "8.1.0" apply false + id "org.jetbrains.kotlin.android" version "1.8.22" apply false +} + +include ":app" diff --git a/mobile/apps/locker/assets/fonts/Inter-Bold.ttf b/mobile/apps/locker/assets/fonts/Inter-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7e1deec31e267585a6c085c3c548c9c226efb9c1 GIT binary patch literal 715204 zcmeF42b>f|_V}wq&Ge8_lEeWQ9r6-HSQc1`qav;d${Ao#R7AiQ6ftq57*>UI5A&*H z6Lt}F5sZl7jk+R+QBFj0Fd%xSGjN`IdZ*j}Z_VuN&L%Ra_xro^`F5I~?&_*n@4b3e zT`M#pL}M1FXf$%z@DV*)_4rf>tu+Vgjm$mqdsPj&kYi6Ho zpEo_yxA|M5PJEpZ4Vs=eX_n3J8u9%c*4BB`E<5+`*ZNNv+Shr)>alprIg=)L?euDf zsMEG9-}jxufi^E4b~^i$+0U3V{o*;<*@s`xex49VzBcW`vnO?VzE~G^MvQ0w&FPco z*mdVOyi(Mep?>e5F=_faubuOc?d*>d!gINO;jD|_z4|dj)VX-05R;4TndjIaJiOpN zQRlNaIsY4>@}?fBKV7VV0UR4BidjCaH5HoHT+3s5lXfS|yETX9Vr>h{x3zz={9gNk z<&XL?Leq!pL6*nqV_BY}PhxqtemBc|^m|xF^mQzw`g1Hx^cPsZtiQrCroYWHZVVBc zG2A$oc%9`N##WZwjBPC6Gu~tQf$;&$kBon@ z{K@zaOV@CPW@@I%(ra2Q>zH*|HZTui+0<;yvc1`XWrmr-GGOu)=1_Ae%i-n-mO+#B zn8%xASdKL>U^(5K%kmoYI+iz@1uP>bX)~WPpJut<3vxMaa^9`0;%s9*UOwwlV zFn6&0*!-B~r{-rY|8DLSniSGt$sfx)k`&5%vH{D+l61<$<>4$pk)H_7)7aCPWd{#w z_H_1iX4%ctjb*kco8|GI<5`~UIgjP}p7U8w^GsuTq31%Db3B&`!*jXka<;GVT*dZ0 z&$TR+^C=Z$~eZ?H%SllV!*oVj1>c%QDZ440vzy-o$dgH=pIL-dkDT?!BGm z-Cpv+d$0FimMgp~SU%)Me!LHRSF?P~`xwi$-t{b_-YClwZ;a&|-ZzBd-Qq=-yl;8m z61sQ0cRNQu^M1zi3-1>k|JqCbc>m@7kt3zvQlVMWlESd+S@qa%V3A8!Q>!V<=2ml- zEvy!N+tO;qc6*B_vN9}6*6M6^VcFfvWZB2+!?M5CpXE?%ILnb1a%+vUMzPGba=F48 z)|o6PS`#@EvL>;8o<*)&7g*${HOrdC@-mD3v@W;EPwNWn3bw;mnC*GiJhm5Fi`Xu- z$Xn|{>mim;SWmKCXHg5Rs6{?l&sxuN=JVF`Y;UkOu)WFpE8E+x?JRd#NQ?DvUpry= zdihAR?Tutk1P8=_kGUt8y`9B`_@NY<|9NMF-S}n`mhnB#)<~hCS5#393onByqPeD z9y8W2nw@asD3)VRJb|SUt}6__)zBNp**3ba$pz=koFQ6HpEUCV;ZJQ@c?4YC^K7G=!1tNN$GxkVZ2p9Z%TWlVp6`ujyCqn4&0-`#dY zea6BW3maxO9HY)mY&%661G_f-dd9-t{kAB>KO*&MGQKd}WNwqWh2i8!Z8f>K$@@*m zH(l7wD*G&K-g^3+=66)SZu64L`?c*&pHn&4_ja%Ox2ezWe_8lUi(xHJYjIlPG)3XG z#lwn9T1-3ahoX{}POAy6m$o^psHAPU-Jqfp{|Lq2ecHX5`uIl_e&HYMFKDm(x2Hbs z^<~-ZGume?%T}MIAGhz@evSGp{kX%@_G>zH=&-cI(xo5Q_Bs5Bnm)_sSN|N|M;$xz z-_^faHvfdNm5&xilKX{`Yt(vbu788{%!h^qLqoyamjUUHJ~V?>zr;4iZZ(G ztdIz58lUdXR4T^yOQmMl>+VwuBfWN{pHp;PsJr^~`tgLZWA5$UV9dRhKdEOwVeAQG z7x(JDbj-b}BNglEbEeP9`ndNSee$yAvi<(_IY*5?>ao7Z9WB&1N8iyer+MJitMAljz=-T7$*saw>7Qlui!!pm&AwjM6|Ed|{ht4#FcRvXes&^n7msZ+ zwuwrkTJQRE^7ye$PM)dGQOnw-BYAvm_hjCV?N^q^$((+F?7}|;r@x<)xid>APD^d? z$$H|# zE_USHmXKHY#gqm`B}FAu)=X)lv?q0F*wT-uM5e4smTOT4TPrE$M0qJ2gx&aJ%5!Re z%J#kgoIiES*XLX3A9DU7g%P!^;nU*$spnt32cPSw&YyZmst}0rYtUqdR`ul2`pmW*Qn4*>HNODgtRShUwxokf2 z6XExfWupt9nf~RBUNf%5_T81R_s`6}X2yycYi7JPW9RI%W>1@a&FuWyk=YN-UODHs zIg!ieTz>uKUta$2!e?-8n_b~gd^%sz=ZZl^8CN{8VC#ad%jPfGdgTvS^$m{+k5TKZ z8u96SRhZ9Pg(Zb0bG!1*8R2siTp7N8!4JvLH9s!cI54-uS`Jdc8MIBQgq+;5lcjxb1&|*Q+{2vw+u^w^2KRFB9 zF373zszuzf;xj(6eaC`(S+860T>5jQo=Dl@3QeYJbWz5i#GPL!>NcL2vbu}jP1uxS zi$@ogD4VDJL1h=IFFY+Zp+s#{XI3U{l=^K_M#^;cXyoG=wnrzVG^3E;soGQc1;1m9 z8dkPuJ7JNQbuM}UJ&cU-ixRGtov73E@lF2>Ir!nn;)f5$4}VoG5U=rhN^HU(Un{nW z9il}1O?=8XpYwSg|NJVkNxNG=SsSj8qwO#B@%jW^*F*YQx=)|1&(rJa*Xj4_U5Ns` zr4Q4$>ObmN>HpDxX1~;^qdP`DV~Ac%`#-3EY#e9I)IT#WF=iW$jXA~&<51%vW3@4Y z_Ww)724b}LCmP$09focEY-+|V(=?@VnQGx1SJ1+*XIw=Kzkv}p8=I|-t7+SxY%DOR znb#TX%^S@1#@lAp+{_toneUn%%n$fHk1v`0kVl~YG%vkvXwbVwv~Q!sO%|wnImK$*~bjZzOt`* ztn4rQo5#s)nQa~~2gyO^D48R3%oF4gIm8?-hsojQiE^YIX`UpHmB*T6V2G^fg&5 zernz;|1Q5W?~~tqTAGfhjmK|p@Eqwm(tO#|$6={eK<*t6VIZ2rv?^^}-jc;4}RV1DEI*7HyE2hUHQpUfYsZGPF7 zHvdT3UbXpU2Wzx7S{`nlZk;YOtSQzM*-^FgmtCzp ztvh8m>uzh2?5U`1;5}vh%QD>A*7$P|YDqzJJm*du(W_T*KrBj>t}IM3mmX?!zH$iz2S zx?#S}=i9LQHgVLJS8^nIR9)MlHN8P>7cYvicuCm$<07Pft8W)!{X-El&Jz4q-P7IZ zwcK^0)t;O!9~8EH2p)r{VI8c8XN2v^5@F8($cBM12nItA90NmOD4ax!_AqVwD6gfD zqTRevgtYc$Pw*z|Ey6Z#B=y(Ad=V0dikr|)Y0s-2C3ciQ^E$TI!}G8KUWASC5|rhK z{MWpTAEu9n5svbb7S+SRgBSJPUGV@Tl;7z)E+IE;Xi z5QMQX4o-nnVLbc+PJ`3o3^)@ez(i1=NpKdN4U^#K zVORyL;SqQg9s^`lJORk5SOZVNT6h}P!FqTG{sK{W7M_FWp#)xlm*8c11vbH7;SJaV zZ^7HJ6+VT}k*WsJ5E?;aI0O!bCeRd`L33yUhe1nd1+AeCw1sxyhxX6`4u=fr2uHw? z5P(k58M;7M=my=P2V_D|=mou@5A=njp&#^zEEoXUFc1d8V3-Y;!W^L7w9A3A)2@WM z@J9&4)o=~W1Iksq4k%Y*Y$0O(A?-%E3GSkXab5iw7y?6K7z~Ho;C8qJ?gVx2-B17# zxE~x?1dCw_EQLZqHWE6B@}UPHq`wL=cnw|$%1fub^v&=lP-gmD@HV^yao7g$!h7&O zd;r_wL)ZZy!N>47pp5m;fO6KqfWO0+@D+Rw-=GI)!Ytt0^eKesO9&aiK&c4Pe-JVy zc)$ns02win4-@$?n?Z9p3|ay5K;J^hq`Zmdhh%*~zT}Az6(JA#;o%-0((fVt9@6jO zex9GuVw8cGy!4WH-V%5LHUN3$C9ka0fpWFTbBp}8$ZsnG}R8OLh z^RNuJxH)pUYs-hRBhM)t;_gJ#?-bs$v%)ocpipT@B(ar7u^lzcWzL_op+h`@?W;8;g_39^Jdb#xh&0_ zN%LmXyqPp_Ce03M-b|V|ljhB&c{6F=oSrkJcQfhTOnNty-p!zIcVG2{}6V-NAMYZ4qv*CS*NU=E}&d&xCGM6t4dCKS0N9pUFV>t z#33aPDRHW##37HIvNCn5rKBe5iBl4BN+MoX5^+i*PD#Wmi8v(@rzGN(M4Xa{Qxb7X zB98Q=-zlHch*KJIN+V8b#3_w9r4cXBgFW;~N_Tjz(j9y1!MIzfb6Z3ceHk?qo2Wko z#qcn!g4OUCJPuF5llW&(v0e*L1A2p=(~xl{dIepP@*IVfJ$b%_k65~sI+*kv&BW0( zPf(ntTW-GI9L{lV{T}yB{S7y)f6Mxx?wN+=+J+CVW&1j~9&UgWm}2DifUCTxMX;BD9n@3;%h^={aF2Hu1%Xpbi7E|piKT|c_4LYA&f?;!CG z67L}K4zl4O@eUI2>_+1EEf@Dk27dk2kE|vG-y#RU#q!?=4Oc}M>~;B8tKYfhwIW>` zJ>uq~i*wP%x#;5DGF_aX&`_JwwA5y{--Ipj7Q78x;T<<0$;?F;=c0>q(Z#tnq`VO} ztWhG)5k`wCO?C~;OK7yJ^*bLunkzb{mu-zHPv+@frF4H&CR(SbO8MZmrfknNeC1M2 zYWPa|k&GE()j~vjY|%Jzf4m3mxw3fquog2{%BcsPod7RcX@`HxJI%aqwH%~_%)xKuhoINP!HW0h!d6x7Faw|p6^2o89$cbA@x05Sj zaz(YC@>T1}Ds)4lrB=6e)qdY`Zs|_^i(TJIJ*&gBIy|exvpPJhvln-^YkAfRYkII# zP+M;Ac?uFSQF2O2OfIpJV~AS~fuS%AhQkOL2|*YOR2D4IR2D4IR2D4IYgMYD;b*+kK7qG&czG@B@zO%%;0ie_u=p#vNa8PE}qfFmIQouD&xfv(UExS#3Wb2Y>~9NGr@V>E*+k`RqH;D- zIh&}QO;pY%DrXawvx&;tD)vrP&L%2n6P2@x%GpHaY@%{DQ8}BaoJ~~DCMstWm9vS; z+4@KDG5igX8KQDFQ8}BaoJ~~DCMstWm9vS;*+k`R)!HR8XA_yT4X#gQ&L%QPzaJj& zFRV+2O?1vCI%gA|vx&~xMCWXxb2iaAo9LWPbj~I^XA_;XiO$(X=WL>LHqkjd(UPu% z1*#9oE)hDL2%SxY&L%=<6QQ$-(Ah-jY$9|v5jvX)olS(!CPHTup|gq5*+l4UB6Kzp zI-3ZcO@z)SLT3}9vx(5zMCfcHbT$z>n+Tnfabl{!_XUs##6Zwuq2K!gh_`ozZqOZw zT8a%CR1NE)|yZ1 zLse2ABK4uN)F)!Eq&`IIL!>@L>O-VHMCw&c);8;lFmIKF&8E;1RDaI5BJ6z#ieaM& z(=S6$O(N3!2ZU)%VTq?;?1;)&4Lhx2r?nWo##NM$kW68? z+=Eg1nas;%K5Z`>6Ngk9XCa+eQ``Ol{{-wjC2S+58svg)qM zd`R9^`AeblQd7@clW$a(BIP5gx>9SZ4u;%?6CO(CQaiA6m(0slZK;sCO}mx3n&nLS zr8U(Q<=Iup8SVRoY$a<)(tAsnqt3gPqh(w{)z^v=-apA#DN0Gas;?DtXqT5bDXb=o zy~|`VF~Tk3IoB-tp(@^Ss;*Wafsm5X%D#PQpS|M>`Azt@X-`wVW>wWfd#XE=<0+D5 zQCq7eR95G!`u*WDJz6o&qC$_VR!hq7-<2*+j>kyp(mj!$^c3%HSyXSSB->ZXlvb9W z3Vne!t0q1BsxOn8tF}8;)t4<2GP*Z)T#blPqhV4lwPYJ9`P^xBw%mH9YQgR`a`k#! zo6W7sVyWk@-R7q1@7`$fn(B1x-D;njtyr>-?n$YPz*h;vaS5Nr=H2VmcxhVXD&nsq z@8zSM-=rkAr0FDO7jI1ZMvS_)8Fg)oI@rTC=$|#%vo-igYp`W&@I2PwBdx(lT7!?Y z1{=0UG)TLb8l$W3l^pZ*Wa2(*3}@wW>DMyuaXuE;SN=;CGf<Mz~r#qq3h@*<)HqZCej1&nX!PQ&GH%IM~bF z_rUfVDLt@Hy+(Ugi+bgMl^1Xh0rA*`52kR}CvVQW)Z?Ysc+(CbvMVlye-bOaS1PLDSf(1UP z19hPu)Q1Mp5E?;aI0O!bCeRd`L33yUhe1nd1+AeCw1sxyhxX6`4u=fr2uHw?5P(k5 z8M;7M=my=P2V_D|=mou@4;%%3;b`aw{UHkmKsF47K`IgXu5>E(9Acf|)Q2 zE{02BHe3pG;4-)Zu7&I1dbk1d;6}I^7Qtdz0!yI~ieMQ$0Lx(oJO~d#F|34#VHK=~ zN8nL-3?7Fk;7M2mPr+Jv8rH#jcn1CgQFs=fgXf_HUVsYC9>A9kFzP8llrMlU8(`E^ zfKg8YMm+@>^%P*#Q$TzQpNjyao&t<|3NY#^z^JDHqn-kcdI~V=DZr?w0HdA)jCu+% z>M6jervRg#0*rbJFzP9wwSl(K4*bv_I>6zO0UhB8I1&QT2|7a;=nCDSJM@4|=n1`` zH}rwNa5VIT{*VO&AR7k4AQ%j@;Zm3blplU<06#W>9~;1r4dBNH@M8n`u>tKGmRC=7$)a2wnXcfg&X&b=E7AOiP;1B+lW zEP&@b3NTtJK<`a}-kSiuHvxKY0;UIiP!Et7Mk)musT5$OQhR1b#gqu*t^fA6D5)BoN_k9|hN|1b8@+Xu0a zUPa%3`~CF4{2%C}PxQ*G+!mU$Fru6Gqq`*$X?;UvCVC(XSucYJ;1PHf9)ri>33w9L zz*Ddmo`!X>9-e`}Kop*Z=iqrLffrx{ya*d1CNfQ~XYxCf-H83N;B|Y-Qk@)MkajlqjL1Twr%XMU8jY1|Ay)3o+tZ%0WM4?- zQE#bOS$h9b_10r@+*s;e-ty-uPkW-jH~mS4E~oMa>xC}u7c7w<8|TNy`LS_+Y@8n( z=f}qRv2lKEoWFeJ#ZtD_`&f*;zzX@Xaei!^9~ver%i{8|TNHJcd#q0>rUs zZ~Q(-3B6Ij&rw31`3;XtK4_LF)n8cCXoJe}@^FnK3=VvRXy4~INsb;lm=PkIHcI6P z(Jk&O8D$*oVmOHNo&2t~e)pZxQ2%4T^Fh>FshD?lFQ)Ri9;LM^M!rx}85b4Oe|uW% zpv}%yV_f^cq_xE9i3mkmBtrSv#=H2mqj}GD4D$!2<{X;J8^RYt9>}i3zUNEuJs7eKnDX%kl+C?Sm1*?P#5Y!eP{p; zp%FBOL*P(o0!^VAG=~;&7_@{o&=%T37w8JzpgZ({Oy~){pf~h^y_iw3wz-z*C)kLN zV6;iqTqinMF@IpaO5dvXL3+C&H9C&6N{onm7T?foZgBI=H{gBv%FVOdLkD=m%@YP* zcrG!lTs89yeG=mm_s>m?P%Zn0R!C~3Z#WSxT0`%#dYgYP@8mP%2(6s_#5?(BYSeW3 zh-vkvNIva=d{UiHJ0PESKtAn&eA)r|v;*>K2jr82d{U546ixa6`S}0&`2YF%|M~d; z`S}0&`2YF%|M|kp)f}$ka21EEI9$a^T_N#iijz2lnN7G~^)nYG&UUz;!~ImW&f$Iz z_j9XaR>oOK1hHp$)W!cHoEh&;bsI3?Pri5pW~~pc8b4F3=UaL3ii@na~q@L2u{- zM?qgW8u~$h$btco4Fh2i42B%axQ3Y#kdY8&rsO_EnT06b5TzTUbVHObqorXnEP$U?=)Kfu3eU%mu8m|5XxBAxY_UEvUD3K@w=ZAhMk z7MqG(UkDeKI&vml$qWlu!Q9gA$VLd+2q7CGWFurPbZzT)AVO>rAr_CN&#X~xR*k&U z?TLAG)SMr$ladcg2k`|mO>#E!7_LNn3d050T04MPz)>KVORyL;SqQg9)ri>NwiEu zP;+54XWa^tGiBM>qGD`OF|)1}Q);UywN=cOv8&lyisjAB3^y-Si*7NqRH2!uFS$rkK9ZDgs+lH@kC?h>fIp4id4ER0~pFghUh}5k*Ku5fV{^ zL=+(rMMy*u5>X^qlx{~F!bn3HX$T_?VWc69G=!0cFwzi48p23J7-$=_>W}FEz?1Hf@WPxz5(T+?DSt)zd_k-MLWDtPm)WiW1A?4EXpB^ za>yc&v&ds=T%xwwk7jqtcW`nzBe!7HP^N zOIRZvP5RQf8;CL7X zC%|Yp5pv-qU?xH8Xb$?C8jMeZ?_}bEnRs9(9+-&-X5xXFcwnaTzzU%VmcawC9LQ7i zLBJC;@x;uP@Gz``)$j;B3Xj3#3C(rWb_u>fo1N;kqgrDHw@E`aYet}YO#StC>8t~t|JO-E`!2@2fzz21pF4Tki z&;S}jBWMhVz@g9tnnE*Z4lUp?XbEkgEwqC!&=tBtcjy6`&=Yz=Z|K9wonz5+%G%~6 z|@x+jkAiKBbs z=$<&bCywrkqkH1$o;bQEj_!%0d*bMxIJzf}?uny&;^>|@x+jkAiKBbs=$<&bCywrk zqkH1$o;bQEj_!%0d*bMxIJzf}?uny&;^>|@x+jkAiKBbsp6<{CGNC8*g5J;v_M#Of z%tU=arc*K?j6@w(wYNoCJ#EC;DAk_1oA(0?;2yXa?t=*24-PB>-ZIpdz*4cPN_!{q zRt0a*Z{qFwO}st7$?PaLQ8uAUWpN8-K~15)(q|zy@mBpN-m2f^-N^VH)l)@(6}r(8 zKBLIZLsw-|nweaoJEhs3((IlnP3A|F8_==)`sT}?zhj}^6jbw8C3~QVC@`a1s;8;? zoC?+7N~$3)qhb}}ko~=i>cdN1Ns`C;mkm1h^vRScZFWM zNf=;)1P^$@0w2_Yx=;`5Lj!0Cji50c0*68qXbR1sIkbSope3||*3bspLObw7d*}d% zLk4t&Bj88~Kqu%7U7#y;gYM7+GNC8*g5J;vj)J~$H1vc1kOc!E8wSE47z{b3n=~IS z*g8-b>H#fT4Sz<%pV9DVH2fJ2e@4Tf(eP(9{23j8M#rDgb-}W86AH{$Dg4^ z6xQ))bo?0|e@4fj(eY<={23j8M#rDg@n>}W86AH{$Dh&hXLS4-9e+m0m(lTM^j6Rs z@M&~>8XccT_cK>jd*}d%1MN7S5jXk~a3lnv6Lf|y&=tBtcjy6`z}$m+FX#<@;3((| zM?*j84_Pn(vSA<$f)Owhf^aMx2gk!GH~~h(iI5AlEA=sOGK__Ba0;9XVoCD{=c`yaehpBJ@OoQn#1Bl4!HsBTMct!dwxEL;h*>EY$fy>}> zxB{+(t6(nt5yEgaTm$pqCcuZ&X*ucmaXKw0{T9fFKf^+}6_)dFG*`fb@CZB#Yv5Up zf09b;)&d{Yfx1u+>O%u)2#ugI90G?z6KD#}pgFXF!=NR!g4WOm+Cn=(E)3+tKrRgA z!ayzzVRe<4xM;ad140f)ijgoCvva5{!Y9VH!+_8E_%ka1qReS%9qJ|JwM! zHvX@T|7+v_+W5aV{;!SyYvcdg_`f#oIU8@zHgAA?;9iITBkk}|Z9G&P57ov)wee7G zJX9MG)y6}$@lb6%R2vV~#zVF7P;ER^8xPgSL$&cxZ9G&P57ov)wee7GJXD+ZrZSdFI z;<>lO{?nF{qY|+rS|X)3u_K2zr$d|5q0L!&-tL?E%>q#E%oU|M_;CgJaRvBs1^96V z_;CgJaRvBs1^96V_;CgJaRvBs1^96V_;CgJaRvBs1^96V_;CgJaRvBs1^96V_;CgJ zaRvBs1^96V_;CgJaRvBs1^96Vp2lzp912aKDKvxT&;kyFme2;;LObXJU7;IvhaQj# zJ)sx$hCXfqcJ>+U>@ybi(?Wi%3jldh^IkJMsclhq7G>w7-1(o%UXjJwSwiB!IrwfH zl_lorIF61>cy;AR6IPx)){!q^;xvm2S7Fqgh4PEa^M&k0z=^>NROtjVg&2z#3E&7n+lDAOFuG>0(Iuo4~IZg$RIyE;OwuJ4-KFpG=j!(2pkH`T|*y1E`0>K^bzFJN03V& zK`wm+x%3g_(npX>A3?4_htWrnEBw$NI>6zO0UhB8I1&QT2|7a;=nCDSJM@4|=n1`` zH}rv{fViPJ8u~$h$btco4Ff^VmpvGAn7{iac-PHE2mAva@DFiVrE)`_sizM0)S;d_ z)KiCg>QGOWjEh8BsJtIao+tA@S6-0F|6ECwMiP}#Wl$~8RcmN1BkV(rln*7^Mrxjm zioJ089uipU=YMPXh0bWb2h=<}N-9eSaBM(Aqn%E=PiGcpN=l=oG)hXNq%=xOdqK2d zHkKCJi?9)1f|ubHcokysnrNZgImXYTg_*%@*vT2O^U-|yXuf=Fp=e><4)?&lyd`up zHDRy%*~rn!)P%{@gvn^W3FPPma&!VYI)NOWK#oozM<=A78O>#*xok9-jb^gZOg5Uy zMl;!HCL7IUqnT_plZ|Gw(M&d)$wo8TXeJxYWTTmEG?R^Hve8U7n#o2p*_4_Z`!|`| zQ_=UA!9IOcl%kDhve8Vo`6D)2#h*`dCsU*HDAf?!DUVVOQL41Z>6g3;c&l0FQPQ{Y zor)^cA4G|l_aUNTY&49G1WcyZO{UgOrq)eH!`NsTo4BIjZtJ+Bn)wN7W_I#iIhwvZ zpRt1;4VwHOr=bhNK(tw16EB14C{bv7 z>bQ0e*Un+)^N<{cmoU22W=`*&L_>*mVbPkR-D>W`+{W?ufS3Xr%E3R`i4A;=s}|#% z#3gYR*|+o^&v~U`588$2=YQyw@_&y?!w3ATB5~Cl(OE=P!2}5&@PY+Cr~`GO9@K{h z&=82mqO%-ymV?fs&lQO3qO%-ymV?f6&{+;Ti+%@Ck=r)V7Vx{!Sq?hOL1#JWEC-$C zptBrwmV?f6&{+;T%Ry&3=qv}F<)E`1be4n8a?n{0I?F+4Ip{10o#mjj9CVh0&T`OM z4m!(0XF2FB2c6}hvmA7mgU)i$Sq?QlOid3{)5FyCFf~0)o!q-IurE-DH^7VVI=|lp ze}y+-Gw^?6uv&>XO|V)vR?Eg}*;p+bt7T)gY-GYlCTwKF#%kGEEgP$4W3_ComW|c2 zv064dR%y#3>Rmoj^ss5T8}JNNzi7qyTl$BogMQ?ym^znFJ@O+*eyo=t>t$oTZ0|aJ zwe^Wv9R1#x_`g~U(N4FrzMVCF{?w~{>pu1!H|)bBz-LR=DJK!nRdt9rCY9bUi{iZapcnP~P*GgoTi>5E2$b!a_)xz%LR=m_Wh=5+;x^frJSpOdw$b2@^<|K*9tPCXg_Jgb5@} zAYlRt6WUkUy076I*a`oDZ{eTt9efWzz`qg}Kd;PoXC~wTt${R(G-=E<(E|8XnMxwD z1ewT5CQ_Lxr=l+}RO2j=O0Q^v5mJxNpNT`ld+VE+SQ4gtMVg1y~ZX+*`zF zBMlm9&`5(u8Z^?Nkp_)4Xrw_S4H{|C#=?`Xt#G~zoN@g0r$jy4r8fN3xtW&rUWjrfjs5zK^Ha4}p0cyZL~y_1|mep3X? z-~m_;cnBJ;G7aBGD~6TuFsy>r@CZB#wA8f6;R$#W*1%J+7HGj~wBR&aa2hQ*?Jp38 zXW=<`9!lT^Y}p2Q(e>-h0j%$7UqO05!8)yxz8<2)?$tQX=h@x>FTzH63Gle}om~GP z@Ev>)Kfu4>NB9Z;4gZ0kIsX^drQp(&AwUBi3@|~02l#)#2LJEZ;Q##^{J&p=|MzR; zz)%M<3Y-e#;SX>coDOHenJ@t+LI@@S?F#&8 zKYp|yKiZEU?Z=Py<461Pqy6~Ne*9=ZezYGy+K(UY$B*{oNBi-k{rJ&-{AfRZv>!j( zk00&FkM`q7`|+dw#(VhDAHt5(ooEF=eyU?y*zmfbGy{DVv`*ESC);FSgBHOrFi;*-W0z*%!6y;I=B&Tf}3GJEPy}3Eszg?hJ|n|+y=M99dIYy1uE?kZOx2+G6<74rRr75|zJVBDOS! zEsbGIW7yIdwlszMzM`PI07e0VMAls&=@u}h7FBjLu1&`7&bJ94UJ(#W7yCbHZ+C}jbTG$ z*w7d@G=>e0VMAls&=@u}h7FBjLu1&`7&bJ94UJ(#W7yCbHZ+C}jbTG$*w7d@G=>e0 zVMAls&=@u}h7FBjLu1&`7&bJ94UJ(#W7yCbHZ+C}jbTG$*w7d@G=>e0VMAls&=@u} zhQ5oT-(u*u82T)RK8sNgW7NYK^)Qw$z0{=`btpz1ig|B>74R^;NIvPzXgzx#>jaQ7omh8yv8 zbieT&!Tyn=gD2pA=IP{qBQ(#E?)$>TiwqMn*o+j!sjvaGw^k+SLXn&N`CFkVLDf_*V{^ zMa92zh?Fol&93}gMo*!!@EP;;@+zU_RYJ?FgqBwcEw2(gK14lt$I2!svf5?IXkPQQ25DbPKIEMLahQLr52E$v#_+{hJg zX1##4{|Rn^eE2gggj?Y@xE=0*J7JN#kr{J>%$O4tOQ8^oU>Q6B%V7mP2oFIqtb~VQ z6|9Cw;8A!C9)~C3Nmv6vs2L1w3cov?6=b;2%&_^+YXONk4g3Oc?WF$_I znR0^6loJ$N;4OF?w!)|Ixd>`L@uXG<>OwuJ4-MR%%$^gZ1rucUoFKF31erZ2$m}^m zX3q&SdrpwqbArsCvqn3NbxUXkt)UIHg?8YF_Rs+iFWsSKuyGuPS6>;xLcS# zC&-(FL1xbhGJ8&t*>i%-o)cvDoFKF31erZ2$m}^mX3q&~{h&W&!2rmHfiMULLyjB4 z4n(j65$r$&I}pJRM6d%9>_7xN5Wx;aumch7KmQ0}<>%1UnGH4n(j65$r$& zI}pJRM6d%9>_7xN5Wx;aumch7KmQ0}<>%1UnGH4n(j65$r$&I}pJRM6d%9 z>_7xN5Wx;aumch7KmQ0}<>%1UnGH4n(j65$r$&I}pJRM3@yQ$gDs?TBAW` z1qy1H12U~$30Lv`T-JYtFkB7Sz&t?aHDsQdfr1(`&&)tU?MAo>kb7-DEPy}3Eszg? zhJ|n|+y=M99dIYy1$T>VtpM(Ud*MEa!2Lkfka1?&j5Et-oLRQE6bju>Xw!W{o9+|Z zbf3_s`-C>#C$#B4p-uORRtziQVORyL;SqQg9)ri>33w9Lz*Ddmo`!X>9-e`}Kop*Z z=iqrLffw9$wCUE-rdy|NEX~$lf|ubHcokys8ocg)N2KUGB1PX3Df*5`(RW0Oz9Uk! zjR?jzA{g65*hfaCu zcS8Y0;C^sm5iACDl)e zH3fMWE67YuL1t27v6*S;RDzXAHoj!2tJ0t0eXg+nu0pIM*jl-4qw7o z@HKoRg2qgk1uwZJ2G=*ZKE6)G;JUQy))~LBE)_xIQs0>-YY85(+z7Ka1&N$(!zYR` zdsC2ci`mTJ6lCmTHnTVdnZ+r{7{+X7atbn&Q;?aQg7{7mvlVM}6|*@7@uMQN3)eBu zG228hnNJG8NxJZ@BKTGje5(j=QU_&yXaEg`UpCU}$;M)qJXDO5O++KvR1BBRIo?7% zBM%c!BgYyC;4Y)omvRD0yB8Y+eNm#gYWMGW{;B09wqN# zn;E1eGe}8#1|+jc$;BL70)@crU2+*b!1i)j&9g>DkT=AGo|W49o`+!-tY!wQN8kmv zH^7Uqk^Pt89lXIfP;Sh66=c?{pa;2N)~g_6L$f_Ui6Apy1@S{8%zhPQ_NyTNXoNAN z*^C*@#xIR{SGn7~t6`n{9g)lLyd|9T0&IX6`Fv^hf<@j-tqJqY>4 zgLL}V4KP812fSc`59&Z&s0Z{|5lsvcO$-rD3=vJ_?IJi7h?o*h3=vHX5lsvcP2`;^ zXbG)=cMpjshKMGHh$e=JCWeS6^6n}e4jIr9j({T}0G*&Sbb+qW4Z1@Q$b_EI3wlEz zI12j0(a;b2Llz8xY*6C}2Eky+p|{spx{9}JR`GVtD&DSH#oINjbb9A_%OzhwhY>jz z_~1IYo?bjgv?s>?GG4)Wo&CSU8}PQfz}O1(2oX8XA#TmMF?x{3xU(wWcIMbsFc+@o zyga^F@3{Y&{e^HV+y?Y&NP0D71nvh17C{j#gBA2%F@lcK>BWprFJ^RlF{9Iq8J%9t z==5Snrx!Ciy;%MNrQq`JxBv}wFu()}9`J$%KBxnAp&rzS2G9^1L1Q=s4uvMr6q-SE zU<3xE(~B9MUhHWDZJ`}>fv(UExrv%0wcKq4bp^HDx7`n|QB0kC96^5oN!nbi2Hho-^J{Aoud+ zbBw(!L<+pA(Uek+V$367%SI^DCqTVOlw_jR7(>WAgVY!=|0y`6?#bC<&dw>jrlYP& zU0YoKZnGLenP1jpcMG#0EGNpZ-jxd_#HlE5M zH8x`t9mXaq`8qB!1~reTxrH$|jL_f?xunt0yfqcGlBfRfQHUoE6aNSqXVI_8GbH9F z_S5U&M?d)KLGx?7pAoYBeQh&+R9eUCvqC1GG%V8pmuLBJ#bJ9?%l5Q}%_VQsYgAbc zp^rzs*SVa$qTb34l&6_iZmJfA_h@#V#M`FicZj;1NnSf#Q)y3sqO>#VUo4*wTIfSx z6?F2Cc=yCJ=oc7ApU>X?U0>z&O;z^)i@Wq3@K?&Xrd{2XvAO)YYZA-E_Abv(T#Z_B zK>nz2i9h-a`731{O>#fEAZBk3t$)(O#4x3@l%%n#-?a%d>Zcpu^=lOH{m6A?U z`!P7F@Ah}?ZctZn!|tqIUCF(b9?h~pw;;8(>-CP{xh}b zexKUnXxet!(P0(eCb!b-Q~CX>p7bm3r`H}V=CAV?D!VOGfZtR7FI3~v>gVm%5tkN@ zTj-kZtc1qNP5qd$CTOeF9^2v6cG6uXL5#a+yUeb)@z+ zy`(k%CG97AbVIT>sgo-7Ub=+U@N+wP%)Ku0tK70P#+My+%Jx~MN`E)ktN1|;TV;Mm zepyOeh!fOFwQ zvu-}tllh!Vx28RD*e&ATIi)+@&nnKXeM_Y(y#}c>6IM*ExdO99sIA z8s3BJhN_;kd!1YL7nS$Za9J-Rdf zNNwBhTNU5zR{K?)T5ZdXRytJX$>a0KDjh#SJNuK;eN4jT`}n)x+RyGfSP81BM%JqB z(-W~ra+>~wn(mbT{qDCbORHP#K2ZLf5cR4`JC(CJ%3pGs*gW`)WMp5ZCbgyR>8^FR z@Ax+?i?g>I>O_MYt%73p1er5u&JrT=!dn#-v)SDoh`o;;f~ccwI4 z>WC{UQj?6BxUJnr{3fi<*b^Tyb)~9n_r=r^_ovhrS7c@;wO4a`E6&^7t^K)P((b#5 z(#m@~{l0szPpkRg`4!hoS<{Lmd%u<0{67^ z^OU#DH@V4URpTG&XR2)_*Qs+;-=){El&_ke&#CkFbe-q}U4UM^)jcKsH;I&ntKL2R z47PWs_SJdmDO1PO_e-P6GxsN@$t$Fm?pai0TIM(#tY zw9#X1r?&TZow9cObDh%6%D+x&`^rbvUV2os`nT1OBvVHOvV__m=Jz2iNDgEBa@Dzb zsJZk~h1g2$t7EnOab}2XIF(aSdSk`;zwG2X+o6%VUQIb%ll4mdKIuKC_73Lyz(~_> zQy5A+BcW~6_W!G!6@B4*bzA>loqf#N*Mcu@Hj=at>Xt~gS4D|Pkc@$|j)ZLVCS zUsm;Mzwf7VV9&H(>g)rtPHJ!W;{$Q6gMW>5Nk8~^+w~nPk3rt`xxenW<^AplG#T!H z4(R0)7yEVN3J3mq_WX+fJJN(!$8Y{M|7srh^50BR*~spu`*u(F+kN}pf&G=c>A+s^ z*SOj~#u|Q&r1Jj4uX5WOZ(3Wdx5ktID+l*k8gq7$JpX@Nt5$Do?ILwGd{xUh+Ap}9Z-Tsi9^TD4`}Fm}@N+B2{PrnX2jxc^vmw|_WoMa$_c2Vl`5iJ6_cxuj z{l2#2>Pgc3A`<(VRiCePXGM=h%G#HP`K6;sF01~g`hJC_FH4Ost`WMplQS2I7sVaS zTm89Mule*b;(dKApMZV}pHBLCeS+RuXKot3r#@Mqr}x&c(|^)W*8ijbtZ&pyjamB3 z#wA9fkz*_~Ru~r<4;e49A2Z%DZZ)eYsP0iNEV`e+Eow3e5+#F`CH%FQyjStM}=3HaDd5w9U@vV8I`GoO< zxyF3j%rMuRAD97ihxv(ly!n~=rI~AfZSFM3NlhB&cv(l*F;AEEWn=RUd8lk+PLeI8 z-#lAp%R%NenIm(|3*`_w)U@SrIozBnN6L}rEP1Rv*1TArC{HvmktfNM&Drt{d4_qJ zoG2%nm&>X00`m$vUCuPGl9$L!%z5%kIoG^ahGp2iLC%x&%siPVZ!~X|H_JbnH_JcE zJIq_;UGg6Db{UaN%)4cwe89Y4J}6h3OXMoK+FT|dm5-UrieC~LGeTApB?Zk;YiTT`ql@uzh29B&m`%j6{M3F`?t+1g?4 zkmvY%_A2DR!=(VAK3 zA7ZYeVm|elb$+EVn78Of(LuZ_UT3*kyvZ`or-S%Ne9ZDQJ{>e4Gr-r;8Zf_mJFSJ* zUL2uy&^m~^+F)i?udC%~7c(!BrCr65xy&QpTDzIq!CPr}GCz0| z?Ji~rZ>8PMyx;Y-LamT%7HLInFVmi3`!Cw_Y?o;KKLzat?M2aD+sOX_?WVo1y(!vi zTeL4lL+vZ(zdl_1QE$#{M=kgtOqPC_-iqzkdONoLdMEy8X&1dK%kKIhmV=oWTh~YE zCy8cCgNXmf-n+-!RIUHx<1)vbbM2ek&Mo#{Yu~q9l7u87xs&8{9I-fWaJ4sSWk|ZQak|cx>+cn9NBgc`%_x(I`&1t)J&iS76`F%fs{MLG{_k7kfE_2K= z#~9BuZqJCI?YIlpq`E5P6}2G1o+=MvN2}5BnXhn$of@ykBhCrxS)4%n94u%{HC0VT zY||9Z8C5T;7ZJ}4Sj?8{H8n@Xs@K&UqKbM`y#;!nnkPc)ZS@Z51?pY!7s3WotXixV zgZ@A*1-(pt1V1a(3eX>`kHKFF+uO0~3s~6>tBqbN?N^!%)TMtV-DQ{eo9Q=uYg0e^*`GwKXDXL0V6)HY6jiq(#e!C8Z`I#zVj zak?((db*x)b$wl5RMj`?M&R6{8;kO~iEb()`c|9+m7tsJ=7^z%ZUIh9-4b*w-5vZM z`Vn!J?hpIiiD*{`;zZEL_2cj}NDl%%SPw?bL-c6SWArSXUj3?mRm7t$o&$Vc&lTBd zk>3F>(C>*%y+|)Y*!T5fxPG8NKzf$wHK0G&pM(BVe+hcM-XJRIuk}Xw+@!w+y#p4( ziI!`*!a`dg zBT87YRxIdJunjKJ)?bBlKgz)#xUwo*6~Vt6Ho<#P>o2;a^-mQw(ZZ*R8df@Nf-AK1 zw}_HfW1Jh6Xf?5#h(B6Qt)`+INeJPRgb<}kLcr&E3+2a}W=#kEqV=NaXw9(R6Q!(0 z)*=xP$+1{mWqn|MDAFN6mWxd5BkLn^o3+APfmD5rvztQJN}S#lwm!l6P0Ctjtp@#> z^%-2(Sf9gnEzWY1))zRc3 zpcJIsLHId@Q=qP~ez1NJWv#>3VLbbf){h8v#5w}{sC5+2_!F$BUkyokOq7Kzlp@V2 zqeVrMh~g?p!~~IOO2CTxb*7{#DQ+>PU`f3VWMmmp(X?S6A!x`~(2Y%F&`nGe z(2%m?29mNsNLk?Ru<>S02XlugLsAwt=pbc5_cp!7&E`S#ptv5gwy&sd9!3j&v*~C0 zf%AyzFL0`ic?>k9uc$}T7Z%?keSwg_h+(`Lk5EsVCq+|JUYSBmcaExvmEpavjW!L*P6AWmifYbA+9y+%sO!oWc+&3 z)qG{X5_g*oW`pQ#zBXUO&qlLJG&Y;fW^orZfUV$cGushrhuIPO zk!Wf5m_4F}*=zQKv(Lctzd2|QidN=(^S!8S4w*x+asMOE5^8Ubm?PjoZ-8Zd=ncXq zy#WZlL9`~lL3AO#LEH|#p^CWGu4>nYE3^kunY0HWv^JSVL^65EAEnuKsjhY%G>hX58h1;TQka-I^2q)CW4(j-7blMtG82)IIr03SMpC;=Vf z17Vyc&Jt0RGzpOeO#7koK zH;JTB!%#z!N&1I~C;dZ6=pXHbg63gZ^N`Ry?tq@r5gLYshA|X420Dhs`QH;nW#}4D zf_@5mh6VloQ_!Sal!q4o1!&SR%H#AI$Trq9%8PGs=8R-*qdaRH<#GPZ_u!DuQ68E< zQox!=CNvME8>eAjFDlDA&^aV@j>n*3khUR_#~F`Ni5^9hKyeWZGa@YVTm4sME_Ql)!QM(B}?&+ zQ7OI~CwxejN1XGhSpz6xkp`fxvDR2|qxFRK1Y)>Y8)(Mb zKrCwmA?tmdHIfCLUi}fAW?5d+`W~`eLzW*#I!S)V zSbw$ticloQLoCIwVkxeuM++H4@>{a}mMp)sSbo=G`F%6X?^-Os>zi^o3nj~34auFw zlDiH|?piFl>$Bu;%98sAmfSU1a#v@`U4tceb(Y*USaMf4%}g^$vgW2au%&4UY-`#= zj-r>0^pF&<#8N!L+yyCK&fIP8hGkfi;KHIV3|arzy}@&uOURalnSGbGFFvMjI1vb-+K@=`3z6U<-CUvMvy=m{**OR+>x zV2NIeC3=FHWoE$-$@K)5>!l#qUk6QcJ;HLmrg_`E4L>B^uPY|quVd*RVd-9zrF+DD zWIlo(LoyyG`9a3t&N4n5GJd_tVCmkTrF#ZT_iRY_jW~;D6J&f#$oOwWHl+JD_}poB z;`|zt?u}WxU(3?HAxrn0Sh`(w=9}7YX(|I8&yS{i*#K=ruS?rc_jl@4(qH60-cbh^J8jAz7kJ zmgp8s^gCFJ%cvADbPD0;yeyA*-ge%GTqlVh$5K2IQhYW1ko>k-e#f)?PK5l1%wjpM zSxzfA;--jX$mwfDj9U}ZTC=p)EUiNKJ7jY zoa{~(mE33DXGJ2*@=7erC1g3Ii95}m4p)-siS7)XRU_R(w-EHpIIl()lkv$c<2B28 z$ueHET-Pku6Irfb9m)$~Y>lORh^0H~F2>oUaPf;1=rQXgjUFxXe2_iN9&V3z%e!6N zyWJf3o>0k9sZhC4h0ro+1HPz&J-%#6iU9|{OtFgWclB$KStXpHu zFGUZ~1JQ#WqMv}|8n55ef22O99*f@PS9+Y@sJH1E)Qg1VLjO_E*8B87AisXmzv`tJ z7d)pwg3Qr+1*DCuKOqUDKgGykX}ucKqpbddB!*sRRkAAUFRh4`qQ4?pp}!`{pf^Ge zG}A|{=2i=R6eEEh^f8hN`V`3ot0c(;D~03$WD0tMR$c1-VXSZ&`gwPtf49o|Bii`S ztqFQ1%( ziW)P+I!g7!I_AFbzHj~P7P&>%iBPRjZR_u$dZC8aDb$8>*4fZ=q34VVO$$vkcIczf zO5^ajTWsj3&@mGiIv)BNz0w3Bsx&~osE=PG{Fc&9) zpp{}ITmEJ{s9|R&GtRX8fqtIq+;8(kjKn|Ni0Cr|(W*%yhyx$QnGjk992Wx}`OJDp zn3CCccAO9wVoFKkBz3=-f2q&^pYwY;AVgAM%JwMU@6#F6GiC~LDJBg_sSV#ll5#I} z#e2x1u*ETH?8O|<^#Xn`-%obwY|K(76(mhYEKL6^m>iRgl8_vebe0jN%;$Lg|3QQl z`FHP?G$U!25RpzvbMd6}lNJG&CananNrF9uq|Hg&fkjCNfk%?C@-lsS(y8-z@t!d` zxNpX!j45y}n_LO+!lP4?vp|o@nDFmmGWH1m`=PwMoDZA4AsKd<{tc|1T>qc?ZO4t$Uf|cO+Jx)+NU$xhEYYrb{K77 zxO5nGB%BmRs=`&mn4JvQ2{!B818(YAI<@1e|XpLB5Ec4 z^eN^CY5S+4E+1$A@F0ZsQaB+z3G|fkbl}X)4B(vbJmA9c65xvPYGARnr8f(&17~A+ zTQNRTf`7-Zj9J4n;QvC*Ldm(1!+uU@Bbuz!;gf_O{d*Wb6HyoX%&wA+w~$>W;u3oF z?_pL}q!wbxiX<>5GI|^jktczl;(XANijlDIihtk$;Xj}~PI0W}`}g9^*p;y#HT{Yh zsY|YrroZRs_ha~-eq~ZB=o=uME`3j9w|$dD`RrdzcbK{ySusxFnmr+V5<>khX4TI^3|aM4 z?jrQ)OEIPE#jYtmFXW{3PU+`!QU<0BLwfR3#sMd$JO`YbQs}#ScS)I@@+SBTQWpFC zlvVVuQ&y!cC-mrl0k@$PM^*` znQ{W}^km8*MvwmQ;_2Uw`RMPuUMVGrGgYM8{|r$UP%18msd4`rKeb9~P28n8mQF1X zPEu;xMf~D^exGifT?b(sXE$bKI<*1OsUH7-8jIaGnvyHr(M#3&bY?do|43oUFB3C|#*J*-Mb`O#d@X?dAJS9g#W)VK2e7h7_BJseMxWU(U}(p$1*fPj<&_ zq}j*RZ9bhkA(dLT3tT;)sZ)rbI{k94sWZt>>YP;6ugirkelPa9FcnYX`z&_%6{)Kc z4`cRz(uY$wlIumd?jp{v)cuH!{TIj7!>Px7e(K4zibSV{eNJ(om(rd#o_Z!tA-7pG zkD{BF08S#smX<-WUEo^WXEEB-#;d3*F2+9=Eml=^h${BNZN7coMJ>R@O^sz|G&fZvee5)s$v+CHqa#lIxD>fXdhDox>+Hf#?yq9Hasu#GeG2d7D}X_WE&+Q#WGh8e8gUCRH@IT;P;UYBCFmxqiQjX6EVIK^H6Q#yNH_C`GO{~APE#_;nN z!zx^PqF9k#Q7_<=F>4jk9%eL0Znn+ncp-YNBK#Dx&j70=WOOD@Mh+p}H)~=R+9+HX zFrqE-Mp|&~kKdq-5kMYMhyNV%L+Fj4c%$ycXG!3fQ|R?HGZvEnj3taK7+2%}x(wvM zHyY`ULUyTj+P%(0+45kKQ(#u?D}XZ8iEOc%H`b0zys0G*hL zT*?g7m})eh=gHNhajk~m(#%=FTA6i$izrSnhRiigdv{;Y^yXrmgPBL*({pW@*;L>| zY0GSx*&fJg%k07&FKtUR2Y~OVZ3yUGeu{$3$)LS>E==1D_D^Y>n>io&@22fIr3v9q zF`mU6;`t-5mG<73$3d>gke?`)B%0dc>?ESVM+=;FnyE@zDWJ2Mu8~z6bny|h;^R5Z zvLKh?-huHh#;%M#38NCEH`6(OJl@z5{td)$7>!QjIu5?M{SV6IROS~F!hJS=EES_= zbpdl1gS(vSg4er59AlWjfpH6=S8JkmhoxoKUT}-`0EM>Z8xN0;l5u{* zJty$vjeU6|-rN`Vdc?rS@Y6iIEs%SWUMOHMPDvle{)~edk^gW-nxo@so^HeW03IZc zCn<29RY?JQBy!y14ye1%#usz z7RK5k;?yNR)({bX#+*CJ6)S`Z#}h{nV9u?~X+s=?EhY$y`+~;uJj96-1xzB^#ybM8 zW4egxL+r<9KM6!*Wfoz7raKVo&U6=jA7eLi)%P)fj3^2E6{gD(-`YpC)sB2xsDr@4 zA%#0uBHE4+ZB7wweq%n~6!^`UPGLHO{VZlb%b2E9B&69!G}iHgc8)VWjp;(B-)DL$ z(|9lNvzKW5B+>S1=5rbxP6PHAfNv!cUyh{I$`=UL+ScN3`zF*poQ2fbaM`;{rmfC;RWkVe2qm zmuQ>nxU}zN+A!@f&G~OPVoqb?V6`1#cc#ZOJ&tJWbw=h`T#D4I#8ETJk9w8w{vmPX zb42Usi7)x-%vJ0f$5?@}0l8WY$iF_voF*K99AhKqv|;2@guM^sekBns%W}y`1US*yppbM%`GsjpnkXAybgrljO%bOzU6tWcELZF`qg4OrvdtpDgC|Va{OU zsBf4vh+{4dg+O9o5@9^kcd^fgjIG!|mqs;$IC3X*9%k1Ee_Mo5j}dKpo~0Q2P|R{D zr@SxultVcb$s;MBXZle_ey1|`wdLZ$*$X(znx%N>SE?xRvj^Q5W3}NicoZd`7&d6m< zKhFGm%&Eussz)5_E#g>(j7u12ldJU>`_CedNjZmA^7>ZhaJ!+a5MOiM(KoXHZte6Zb@i!%WS{ObXUfFc0I>*2BFoQQ19im zd39kg-PgJno}~Gl&~3p!4|6)dW!%c4eqnk)^h5GSpwp|h~J!{x@pzpx9^qh z-^izCO-yrYt!v3Ac4z|SM53|#fjD0AMaCOAwi}r4MJ=u3 z^y@!y*!!8^l<6Eo?Cc>N!hDvklHZ+GnfO*^_Bop4A^T~?F|=o& zt(cR-bPDlh9*5%cZ#8GvvsAxyTc*F|=M6Fa0HIpUvHg{Eq1~

%d6Uvdqmn@rfHRiJhs}o3;8Jk1#s#0eR(N->}>Id?v zejwV~OtiI`X!wYc&GizYnl@|QQ6%)aXFy+3T2utTtk6wv=@=HB+q7#P7KI(!wF!%Z zu-$M@BvZV2K3X4cP(~fB5D$qM5sOvg@mLL80yU+SxC$|p6XmhryrQ@o`^qbeB!mo$ z2*(lrm*Ob>Ux}s6eOK&@JEFoMM_l0qYw1$rK&ypXw7R?EnqFx z0C=5h2CS{xKl)hrM^)!XAM5w1${9eRdJVa^|HG;e-i+e6Bk+E&fgPOd1Z!ZT2DlE~ z=&jQ`r#Frk3M>3koZ#kvh?fJN@fo-h?;h}|eEJ{abq5c{G58R#2Y6IJ{SWbaf`FI{*kda-w*Aq^zQs8P?C9F zKq;mm`=LIOmpd6dGIn6RLp&(9>1y_V?D{CGFuB5<3M(s(t2Cq1^5iq&`Qg?0?uvvX zjUqXi;hvm!GWAXT*5G$A?IbqLo=nTauQh)C@tca@sKc3M%V@hVT zy5s8Zt*7c$slV#h_06!P{etg;W|w?#>vG$~3%+x1JKnr@^StJ>TC{9g(6XqNYL(Wy zX6w8*wcAZ-+qmuKwkO+_Z&#~b>vj{`t!%fYeeL$0+V^e0kun577yC-PiZ{)N*eUwF zctK1R)5LV~qL?9G5{2RiaajB)j)=dCqv9uVO#Dq87e9*=;_u?5_=h+pei5g|ui}jO zO`H|yByv_Nsj)fGNLxD6HB03r6;UZFRi&wPm7y|WH6|O@Vqg_QRfkoGYgA2Gjj4tG zC$-h}s*bur)x|E9`szm2K;5JoV$bJ3wO<{;uF3D!A@zegtbSBS)L+$6{Re#)_K1MS>wC25|?bLR)N3_??u=DdS z?5DX?bk_Il`(a<9zwQr<3&Zp<(F^bG9As#nR zo5^CBc@DeVbFmk0niyr~nR#NYUBWIep0Fp|lf^UGy}dw8w%@mxi0MwMlO_tC3@1~} zbh4dnG0UmuR1>c{U7c=XwsViuUCeQMIz7c)=RW5?@do6SSEi&cE!v>`%oX#+BC%Ag z6l=tK*fZHF_TtHpq4bDU6bYRu;RJ+G5vIS9Tu|b)N_eA+xc!cM*0ft%MDt^*T;% zQ9DtBPG|ue9C5-@VeHRaYyK`2_T!xdt~37teu=Rtg`M}m0KYP)fg8-Pz^~02>{ncd z-Fd&^-zIYwxY?Y;j@`ZH8#J^kid$?6f6L8Q8#3E0HQQ_rw{L9=xZO6`*VW1Fuxf0^A zsbo^j0lOqhY&H8vyQOeUsyS$vLiw$3H?Uj5M`e>{zPC%GJlC*qvRi|bWYWzcy9~uYAM8Yw_L}z1c3b#JHkszIeHBXowRR)B9eiY&AMLWJ3$^T9?DqJdZI0OG zP&2Nx8)NUkYr>|A`Kw(XHKn%Q1fx>gRHwUQS854UmC_8Vq^U+}gK4K)3_RMKPats{U*z;om4T7BLi>6LzwF zgWU}DOmiGo60WzKf_{a3*>!AKxgj4wxB)4w6m78I?q=*+@1S~O|J(%ZQhyEm#=pQG z@x9my{txW;z7hMpZ`GZ!tNStR<9-!8w-;cS+!$Z$f*X{z1tAGc5lVA--c)Hfal3I&(fY)JPX;e zAYH+e1=wo}DLYWaSDcSsgO~ksYoT~4#GP4Y=p%T~HgKdn~I3GD50arRJf$JTNe>fW* zNGj($2fIz3z0O|X0S6-l&JPYO5;%WzP5}SmoC2P4s3(X0Y*1+o^_5*0oet^7y0O4` zHy&8REdea;mIl^$v5U-Y@3sft>2?Bkak~J!x?O=eZVvEq7uFTrp)RZ|xTD?Cz-8_- z;72af>8^B21qCX@`kK6E{>QRo(6lTZs_ zt593u?V-njgF=IVABH{zF2n9l8TvAW90+|Kf(?Ptq0k|`AqkC;-jw$hXm0~44m~MO z%~5Z;MX+J;q525)QtZ!MiV=W?@I`OyO}wS|@OGBtt$c#Fu||E4{P$wS&eM`v;$|^M z_LD=<_Rf|Yl!A|quu#-cuhi>cQ!NWS>0P3&2bZ+aIOILuyCHBmVs~-RM9~EQ3PfXIzG#Ygdf+Z` zNMCt8VH$QmQV3WDLE2gY$3x~)3fltn5huk^5!!PG^y5zK>rcQ5*dwg$LC9f>`wzf; z@kfrk9C{$d^S_tqBl_bFj1XgBQ)Ch>mrWNl#T+qDEEG$`3Ne6lajc<9(IH`>2H8Ll$~ewZ)&o9W0&!jug)U zN8_33p3edYp@dPGDL^U*#q(|scu%72QHWQ7dEzzT2r(O2AYKLL<4!4PH*1PIqJe0P z65LjFL_X&r-F_Z^hFlnd`+oudgP{v4JX%tJbQX;9m4wfZ>qh$%;AW7xB0K3GgJYDdl z64DQTQReLl5;72eArW@CM5aa+m4kj*@=9ywyTUYABk@>g?+p z;AmMJI6>9`4wAKi1@c;8zN`rxDzBs1Wi`-aP-^QSbTi~)C+z9%g;FsPrEsj6i1#)X zdgW~KrdS{ri{)Y!a%KZ^3Yw+tj2zBGn*PMsp|T5KM@iV4pxQ3mx9nVEA|r*U`8Z!2e+RG;lO}EU2;aDPW$YIy6X51{O%FTbI;3ISt&=lFHC{SqPjU zVR;QZve;04}i~H@J>GhXR!PPSRhvbhsuwEqvT4U z|4u(+SE~DCE~=N@W=N{#d2$_agj~;lsrKhfuWeu{BEM$W4aA3ZDc<_k7mZM(TFb3) z8!Wd0^W?X{5%N1=f!qPi#~d7`Z5MEq+zuQrj{wKX-N1`ebrih8@)&Tm{2MS&9tRf4 zpMd%DXMUq3(a{dzjkZT!?((m^Q|c+oUl7M&`75OFck(yjE_oKXTa^GmSDprrl;=n` zD*<}6QlS5)EZ}ivfIlk`n~rGVqmFNgeAXf5rrsx0tn^wB9VAiovK|FK?;R7s!*tIEJYeygVVSD=~z^A%cx z3+1<>_G7SW3mmPebs4W(1M^fH;6?IVQJXSa-3^?ex&Q~M&cFgiEla+FTu0m_zppHR zE|KBteuN#Y`T=v*gTRrhFL1Pa1UOy|;T#>xIXaAU^a1em)BxZFHJtPHVbH_Wy}%Jz zDM9J!1I$C1t6AVasi?gftNRpUgdm? zN;LFZ$mKoYIP_^KU2%c_6qRRwIac%GK3GwUkPEw_lsalJMyq##d9WTv`7#%=hZMCb zBh=f#q3Q$RD764MT)hE|)_g@Zf3%{Se}U{)Ux71Nt!LLSfrHcrV1fFY(|wVCqo@WC zR^PGjJ-`WyYV#mPwYflT2j;7y3!eA{c!SkRcK^3D=4tV;ohXsT_wx+HKU#*^so z5xn)k=@j5`oeKO}CxD-)(?FllR2zqB)K0gA=31_k!70!c!6~gPfX>&b@knPFI7*|& zx?OY$(8D$At(&8%mXFg{gI=c5TG(TBCD0$~^1zi;<8@VVFOb$cgE+bhus~-5hw3cg zD4h+AzF*xC^k986@G{a`w_(@Yfw{UJaI|g<9HiR=hv_?j1-b(;A0vNM&o85A>0WRj ztRF%?e59$Kj@Era|A0|K)MZWeF;7z+9Hgm+7HF#PmrD!kHNv94BUB!6f*t`J1lw{b z#X1*Qphp7p^(amYN$h{Ch1X9a^k6*+I0P$+=$R)1M{26+qjdo=Pfr6*(0>6A)6d~P zA^m4yteygl*QoD!!})L>p=SUK^b5d=dIE5$ehD~AKLs4FUxd%D`WevU^wYqxdOA4E zG}>&u+o`|{q_i#sXRw|HEYL3lhw7QYQJPu~|6R^u*VmYHQ4Q4dz#Xiq?a0%p{Yc+@ zV1a%cn6KXg4%P1faeqkoIK2?`2uI|0t@tJV7}gl{OJ)* z-Dz+J>tBH*SlT^7K0u)yqi=hYxJ9%;FQ=OrkFo8S#iy_jnSk-!R9O`x+f6W*I}lc< zpOi1ay7X*Vep~~KkKe$$<0(}N7NRd638T>p8VmDA!eU?@`9_R|c_U#@oF5C*zqofn zukuE#G}0k;5>iKVJHa>x>b_@5%W|5c4#2u_)W0-Kpj{3@Z+0R^6K0|hu?W4`wdjlO z!l?9dj7i(Fq^u~@WDSf)H^x|WCz&IA%K>WuNdzG@IgsteQl9Fb2&lUP>W=}{IiUU&P+bD* z?ttnVP~8G5C!p>LsO|xU5*STO&w#o&pwQMuUGEDhv~y7&TE8fT_AW}HwTn_{{i4)E z0YxpPmwyijRKI|FB%t~S)T044AfO%#sDT0Xct8ybsG$LcRyKOy!2vZSpwJRWUC{!A zx}emHEVNYuk{~GC=mYwtJ_P>`{z=r2z#XHp|L@9vOf*H76B|%*0Tmxm2?13iph^Z* zsemdSP-OxtF`%vrsImc7E}+T>6f^?PC(EfAP*(?3rGTm&P)Pxm98lqaiUd?jK&1v$ zT0o@-R7OB$22@r+Wd~H1fT|i$)dH$|K-CDSYXYifKwTS9wF2t8fT|r(*9TOcfVv@| z>IPK3fT|x*HwILLfVwH58U@rX0o5>|AXlRAqj5kriBk1{SK17O-Cpbo`gY!B)$>J!zmn)nIy3{byNE6?-G z7vodyLr(lZNwYsAfK?8(VvX{T+YwJ@(%J_|O`+vP%pnisC-mA4)KNTN28EF2)h1l2 z?NDYMruDsNgZ19?JoX_l?isW&=UX7^Yfw1M^K81DyED~yN6eJRJolv1paGS{PhqUK z4i>dwZUkSnVR)0cH)g=D0+urs$qlaP`eOvX8v11SL#7v^53yMs!Mt)sSrgiaKZ`tF z&XtQHvo}D)qWCWUQf_!3c|{*syLCx8O4<34mv^TY`uFa4Iah)4ybHq=zt5#^v{ID zk?UsIGcV$=_iR^RnJYxqW?fX(=YUyeiADY)x}?o{*vEJptP$n z&9yQeC&1V(FxS`0Zf&=*+uH5y_OKJ&f#amM5p!?wjnL_Z2v2d~+!tJ_txO3xP?WJ| z0jt~ii~vYz+=W)1cp>;sn4?L&?8=+Vtj0ljuT>D-DrjyMY>qLe*we~%sxP(hgSBzo z3(^zmpc4xOo~Z-ZYVVCuyNH=uq-rwz^iq^1N@0$AIYQiswG=mDt;Ee(4RH%rJT$Ry zvzyz^?53FQm!cGASO0NYc|&vWJkwq*)-+K9t2e4)eMSwezo>~77PYX7qBhni)WfQR z>ut1s0;g|WX4e05v2_S(3RXH5nwM$L-^?!2VHc<|eTjUnK&J4o{!eQ9@PZ1}uF076?s~WLAVp`SHA8UHXN>@!%Q)Ovb z9G)jD!7l$wwGJzKim)E+ggPrTw5v4?w2jww&8mo;G#3WADR8G~b zi&G`$QL4mZN|ji7ajGzu%EQL1A=b%;{P(}40_ zJ0icm5FG;74#<6+c5~+5p~RW^}}{V&GUFULD-xiY)%k1CkUGp+&3o(OK;eV zr9%*w-nGy3?~583@cjFB3&M5_!gdS7QoG>A(k%!J8~os{=Cb2mDWB*+-I1;ohOTrs zx_bWUt!F^K{NKIzp0W={RffqiSZVSc##Ls?x%L~;cikzVItEmSD1~5FVB@I&y^@IW_okJs~qb6q9!)zGAL29ieb3<*l8pg7vly zVfVd*>Y{q7ergEpx=&QktC?6Mv`DRhMfc5W7uHK2S7)$ZvZSu4({v487gpU{V|`i< zR@n~FBVf0EvYrlG?ek&9eHG3S*{1jEBUmRSV4pqFO2T@fT2=$A87!%HhIRD5)*x6~ zFR-Rqh1Ohap|u>RWo^WXRtI4<{j{-60#>%AVAWb3$eEUSwys#M+aIe}$D(&O71C;+ zS!`Bfo#qytd~n#DFlTKSYZ@zIg`=2*Sd#p&hrbB4g$`b3;pFcbFE7hyHiT4%Gf%Q@s6 zch0!BTN3us(_jm|uG<*)&pTmlSZ{ZLI|642Om?Tcv)%db5_grm-rWY<|3_Txo(#o= z5<^L$ELb^j5NZ}`AL<XnAN&Xk%z+=wRqr=rj!4C&ZMG zNr|ZzQzxcTOv{*#Fljcu0o5U(xV%w5S2k4~rI+ zc_e$huta%biSoh{<%K263rmzA7IZWgNUnhL!*&nC`uR-q%@6D6Gf6r>EVUK%Y{c{L zORbR4^TYc2j5`Ej{d^{#AJ)%j;`w3yd?sn?-Gf`exqP%=2$`9-3GwDNqSU;bM=ZE$4nRtF! zKc7j1^27T1OgulVpU=ee!y*m*j*z24SU;bM=ZE$4nRtF!KcC?ig!S{8cz#$vpNZ#( z_4AqZIzP>RJ`>Lm>*q7^ys)@yG|fbLVTtncnJ6zTQC>b1<%jk287U6J`uR*eKdhh6 z#Ph@Y`HW`_!ut74JU^_T&&2b?`uU7p3BvmMOgulVpU=ee!y^6sj*yQ*SU;bM=ZE$4 znRtF!KcB$~!ut74JU^_T&&2b>B4qTwM0xp4l$Xy$d0~n2@|h?*q7^{IGsL6VDIp=QHvm2Lmi)ZI|gxm|l`uR*eKdhh6#Or2%9MxjIYxn5?j?rsQ^x7eM_54%Ll7Grq@9O!d zJoWy2{wY7{Kjo%(_54##djCEDl#AYf&p+iL{ipo%uAYC&JMX{epYo0VQ?7Ye&p+ju z_uunRx#j)${8LWRf66EC>iMTU^8S1Nz5G$^Td`loF)NcJF#3f1;>jpykrPjw>4L@% zEv!pU!Z;bmVen6hE~*HdD`~f=_rDKD&SiX3@V|jt0h&g>u8sO=iE%lPQww}WZ}|M! z439h~fG^F{K+Ared|cLjI_{1Y4E750!B{LFIfBP{T8myXOE#13WoOw(&{~O#v`Sjq zqNa?qmp~hN7T-zsTgWX?_6K~OWWSFwx_S0u;M*97j>GuD5scrRu(76-{l9~IPU5+W znwZmUTC5l9tr4Dr@rT)1rH-9v_Lsq3eCow0uMl_kc!lWM5>?2t`;^b~{T0IfMSE2= zt=_fJUKdT1clF#65Boz1cBdG~ox-ohQ+;ZG0YqOKxrF%+n|8uH3pcuwpEY>C7diit z6L?a36UYrvUY;Wk#7OvJl!N%dA*66F%FDAD?<>Rz++2Hw{V|sv?Cn6lEXHc{6BvVO zfVa{Mb7qq;?y?9cf)&YQ=pB|-S-Ky3b`y1>o~M_ipLG^H%hJ%lYHjDBXEhOhs-^Zu z`!EiD3_Eq4w%F@9#yyNZEgeI>(Ytv*G(WU9v^R7HeVJ-8jbl2;^otpXp39P$&0PLy z99AJ6_al`;U^fg(S}t59R(E@2wBCrd=YlcUT1H(k9~8KB03OL=P=`U zBFZz!D}0?Il?!fBl#6YD-q%@+6_3(c8|5o!f$D(&3vid^;34&@5&jqA6u4ekhvIQ+ zfHP5O!ob)dckM4P&Mp^;LuoyD;?}*!Wvupk2>x& z!nL(5!qw;0KwYjcd&>2W*Jfy(9=`;$-x5Fi;8}4pd_y^hW(@J84|ZqaM?1n}u{*-W zN}sJ5;rmt`5?^4&&l6$?PMP}=H5{kG%cU53Z7M(0YxO$0MsK&$#NBdK~@qO=^(6&EBqt*+uptHPZgkKBmUul)=+# zqBGK&r2ayupQ+bz`q@-9m(D#?^PQKRLiLU_%bBI#b>=t=)k5^(m#L3&;@Ow#bGN74 zQ*EL%%G74}8+VKP2EFue)mHaA_dE5iyW8EZwui!@u-Xwy38ko==(A_3??P2VRn)Ff zjZh7>J9KU6I#q;odh4lubWX220Ba{Vse_?mp<(L#(1_3obtse@%2hugmr(XXKO?88 zbmpK09ij}xKjm)6igpR~{%d!wyH+#?$1fpxU+!XrqO!_w&&9|Mel&yP{l}T*bfy}& z-%9)u>!=d(a>}Y?oH={5>Mk;|?g;yUu($9{@gR13y(^xj-Cp7atgKotUc#u(Ct@ac zeeDsm)la&K_yp^#Zk6TrZMv1Lpxfv>WfFE(|3PMAU-chl7S>+%m)Y2f_=K#76Z<~L z2s%!Ql~3V}yrc33bJm+b4wyM6)x6ZffVP~haQ}v@$=+q-Fij^ARI{5 z-lo%<)qHocyF|U?u5-Us3*E2Wuhb%UtGiXb?{0T@s>SXucbEFm-Q(`Z4!cMwqE^6~ zPNw=elpV@et3y>o)zoJ=1+u34oKAsMUtnikeYK9xf>dA9DUfP?Xh>+N`YJR$G+cch z8W|d?Hikxp#;HxA384unk2ntxO7l&15CJuWRxHQUHnAw6A1e7p=xpnxm4r0%S#4cEmcLR8Q zZY^ZDq?uBICOJ7-y z`3<7{wb(Jc*89ptcA?f6_cim8y0+pp^BP8Z(-HS{t3LMnQQS92xnmLcSToj2!#o4= z8%6nj5qDqu!u}4`DuI<4Ufdmp4ZVuL`Z(kvxiyZu;c4|2tclEHZj&fC1@}nNEA$G? zZ;z!H6~(Yrj#lA@ym_zZvB>e$BzLv0eoB@^H%VNYskaX0#}8=9;;p0-xtw(SFc= z5cAk*zcG{AlEc}-uIa{xN`l0$9f{S$e1(te=}t6f1@1U-apilV7GX}KKu!U_Klr$U z--2U6&C;C_3hPOk+mhqxN;OM21)nMSZUw%E-VDtdE?DQ~QE+L^@zepgGBVp(oX8xFq81m8`ZD7wXb!X~C`NJdIH8@Q+S-mttG=8htKa6DzT|PGUt3#ZN7U z#;(tW&|V}oYtmwfPpyW8t}SWbB-SqwB`~W>Z(%HUS4zwW`jimVc4+MA>MI7p<$kz$ zlt3M!c)H?8>FtOw(Ow&(@HWw&aT*##X@M)nm4Yv|DzpZ{r-ir)9H|!|e$3|EFC!j$ zvn7nL2YPe+WuYg}!j5>ahcycH+X(0C1<)S7z8BfZpl79W1OWw2^cwi`chO&YuKfYj zG~cdBp?TR{FxyW0xcxhG?b!3b040@X+$GPrTRh`#kfshuG5Ule#Fjncc*ea9&$uV@ zTziD)+F`dZNb$e6HsC)=Z_Gbk8_+enHlQ@8w9Eyo0?wzD-bV49jlPZIweX_15rdlS z`BLm+MW32V3iXrAN6Us~>cc116U$&to~g$a@oELU+Dz3lwK4ld?*1FcN{!2|!>H`r zk3p%>u)k0RZ>+khE@EI^<_TU!Ry>Bw*=MNEc4)@J_m$RqoWIBU^d#UJt~OO+ZHA;V zt$>NZMvGThxQxoCI6ZtVY?`ctEtB=IVX^_XOE$t{3C=Z7CMKcMILqd8*!$~@v6j?%y9eiY}8A2WY5{`F_`Gv?`z z`&cYrXCRe-Hz&LTHhmn-^M&jfJJyb~_~IQt^Ze9>bTfW z2d%kELwiS7uV|GOl{A5U{OEf?Qa}e|u0+q*ccC&$D=05mD@FcgBkgc#uYIG^^&za* z;0LZn?(!6ToUd7=r@7>2&xEezz_JipT3NisDTFM#j3p@*{*UZH?Y*KMs7_b11XavF zN?-{EIaCsRRi+?gsP`0u-cvdBn>MK}BAYEORcA{}HPADlMR ztTHPnn!)-?MR6PEs#8RBvbrK#!0Jk-Xa%b)RUo0y*VYVCGO*DSwZ#(Hi1O`2N%$el zE?Ct{aUrHEWN!)9ma36`C0JLwuUL$|v6k&Ww8;f)Ggq>y^=QzxP#GgjRYH)Zs?wZ8 zVa}m6&Y^VHU8|#1mB8w=h>3`6&<>@Fny?Jj72^doazZ7AbzU_PbZLHyyYU9Hf}U$R z^js^T|C+3tsjebJ<)Ht1Gc4mgD|)~hPJQtZ?BO&KFQX^=h?s?bXNGu}&T|(Ft)^B7 zu^y`lo)F(z)2!)YAD!DS4p<*qABo?rP1YuHmQH3D=dilqAd2e`)?q10BbVBQOh{U= zo0BXJdZk;Wjb3I&7>jA^wv{n-vbl_<{-TVh-l0su+2wO&3F;ThGVTs{r%Vi`hcabZ z^zf?63e>BU6*2NPT3#I*8yYJshgO6>mPyoOlVLi$Tt@iGy&492{6G2rkh}K3^4^N2 zi0XVLQw`S39srjtG^IFcPPdDEB$*npZT2AhNHpmvpWOo=v~rT$1e9Saw=`boy?t`A zIZQ>A>S`C1EF z=!VeDgSeU@w*RnB(H)EDd$HIle=D2nsP`BT1YO3Yp?=>J_@F$3CWHUnE(LrAoj3d+ zwwhtYt1qf;6=2ck{IhovRj(+Ai%a2uMF#uwlw~RIQT2*aw79jaf5q07Z@{pV+qd3w$I&m`oQLd5PjS&L>t;D_v*%M3${elr#&-jV@ zNuAS4x;<98OUwc1nqq(kT^Fl2WRsG*8J%xi?j( zn$+^CnWrPsY6&g_&hC%i#=`o|)q^f66>v)vj8mYNcw!s*S9Eu15LUFTHl|^%_N@D7L6- zQOlxsMO}(=iXJHHU6flirRd$FWkm;y4(_S6r^=oi_T03m>ztEa`$L1e^mKQa>?ZK z$>HRzMJ7dFkIav}8(9*~u1s;GU@-9`5x zmme&8w&>NOg+dZP20A%5HLU4=L| z_2l|PPY7{nG=7+qI5Y;onuo4Al=Wk-5CU%S=l_Z`{y<34CI z`_}DyWM7|susplJVDGBEckHdV=f>R&=??y9Pt5K|_mo8m++Q@OD8DGLsP66$cQ4-k zvkOeHt(B3JJxSa?QzXL8**l5>3#lvA3 zM;VUMz{~Bf$i>JY&74lcC;-Oe{x5d#H8i(l%;qgK*S_!nO8Pa%cx@9i&3@L@hPHa# zw6XqX+MAE<2h2O@l|5x%H+Pu%c3)WEdx3iD_Pu%>Msmm51JQF|Y9`uuVnnwSMlcM- z8TH#}hWRFpC+5KRT6fq}TMO%B>%?lO03(ODVpQ=q>~g=w`~|!9Uz2m>>)0u`Rd3h* zRXps`N>xs|stoq(w^a9GC&mNnUiG$`uijCwTN&yU>;lH?1Rc_@d0xjr(mjb*IA4Eg zWx`59EG+26!Aeg&Z0NLBtwbBuTC`PdL^}nGUg|DqjJiYg#0=@Z>Os*<^%3{MCJZdR zV!y>x;u%g!3VXcZX@-014&eKoJxAi1BUq2&1 z*Kf*gda3+Y9@U%VPkOUFroX{FR3p*M8L4h@=DA;D)~}^%jyc&oow2Hu>xd!F>&|#F z!TMP|tr*m00H`_aQk%-{ib2|Dqppo^&f?C(Fz3 z5{x*{)=%hD&I0V5`9S_%f8{*oJdM@K>87+Pi@B=nLf^Z?u{L?68x~zvj`M;{QEQ#) z@@5T7Z%T-bYLRTEOUrlkWVt{;E8o@6$%XpQ@;yBTy{{6mkyKK}iH5LaG*K0Z=hQQ@ zq*{-;zcR9oPIM-^De5z4w747nwLh!LVxIdA_T5NW_)J94ElKu-eUwtlL65P7ctAZY zdaHh76RhEER*Pj@eU)sd%gQ|6UXIqc%SHMx@_p>dnWTHm>3W2_#oa1qsX}*~`h)we zyIr2p8$=lugSpH{WP4ps-mc5b4!VN8LsyjB^)mIX`c)57OQ}zWUe)dDHW`tx=zI0o zm{-^(Zk4Ot-D0rnC1cfc%t|+r6ZCDeNH0<4(d*l)8e>MVh3cs8MDGthJgd3Y!fIu; zRLj(pZVl%dYZ2xsr>W=F3u>PA5N0;FpeM2wvx~>o&+2dLjQYiSQP**1xP9GWy16@3 zx4^9FA9O3-l4d=1PxorKxtruxf_Cz_9*>#3gIFj3hW^}nPjA%UVn#F7y%uws*So@g z&D~?&=1g|?Vs3V$Gfg*kYq_;mH`QI;gIUMLdaxSrX1M#@{g?%vu3j`>yEkDxZ-@Je zo8`QQvAexyh#BhqX8Jh4x)a?3w~kxa`P$ip73>=@uQ9;w=w`cf-D;r&Zg=-a=VRxj z^LO`Y_o+~ko9K>m@3kMcAF=z}k7MP0FL#1F#;uNV!kuQ1dxQI#`?>oCW~$e?2i@=8 zL+%e)3;#!ZD6GQe!Is)^cMxoB=EM5hD0{3u#2$^6@psz8oC9_)tg!wA_E=B34V+Kh zcin}~dix1?lKY;!z-{Q>Y!_f1$RI<3*4)dGf)&z_ zog()Ex3|10lomRSo_aqxYUvT5Ym7on`|n*_abY- zLbD=b0YL!~5d;BI#3-UzuMNa@#f}9*rHM+ja_tQZDqsOCDvGEi%lmuIbGBxa;Qie9 z|9RhgKVN^#=9HOdo|!ZA%$!+$rLo2M*w~7ykwZh%QD+|MLcf2DV7><;Z2oqjbi-X)wQaH zy;QBjFW&6HZ`^z--!ncmGK?RL#>OW`6XR1OQ+wD589&-p_HK2t{k*fsu#6Y&`NnJF zG2?CVgz=7jh5d`HG+x(!7M-*fqO+DS!iaCp*RC*1)Wh~;>Jj?|=QsN~=NF^Y_(}Fw z17sg{xV%8`DW|E4@p$!nnX~&A;*e&i9 z>>D=%`^BBAoi0wpesbq&Q^h3g(RM!eb(^eB7gG?eyAFG?EkqRVA?%X&uy((A1UskQ zuPqQuu&3H)tS)&~drW3(Z{vN;cVw3KF5bxeLKbSb`sH%5euZ44-yxsX@04rxyX15F-B^8ek91zNIgh@959Scl8x=tG-VDs&A2d^pE6k`p4Qs`ds}f zeZAIHyIQ--xfs7Cc8SQ*{2~``faYnMXr>vWxn_zMnkDizT@+wn!b0py7?crFBoByU zSuHNW{&!Qgi^MeSYj>e`v6!x5KU{6Ln5kVVF4E?R53%>#7HzFOMK6*g^kR9c9+Ic& zCGvE=RGy)i$&q@w9EJB+&(tgBXuYKzqqmZ0>8<4p`oHBm{a(3VUo1E1_sNa={c@B3 zfP7JZP=2Owke}-t{7{VVvO1HAXsD;x``W>0OcdTdK;sRGmX*47~?Fw7dF-yhc`uEz>M;Gu|aGUo5YL8 zqj*E?apMVNsqv(-%vi4ftpB3_s_)T%(|_0Z;tjK~aVXw7ZHr$E=^*+z*I>oWb$GMt z24lQ&jxoVF*O(~gVwK^uVy$?|xe>cj-(p;BTw=_2ZZj@5=7_JbqVyZF(^=r$?%aX* z(QY>uId>UzjVp~ij63n(>38ybtZ19<-0j@s{2Onot(24GWPP{(wZ0GUZr!iK>M(Pd zyE=Ec^R;>ps|qHXQ`J@GWOIsnftqJNZa#q-+SAlj^D=XeIu!4sjkHFY)39^)baSS8 zkvYrWX|ypayEzY<8_d(qGn|LieCJ{3 z5od|>DBc@;+=Ot^3Vqxzb!^u5iAxhFhmvr(364Bdk-f zX6-(8uex0=z`JsH;f=XF)II7}wOHMz7O92!Rh4P#UG={+aobxe#ko{ZL2Xbz`+NHb`$v0=(o`1S z7;L0YR4=Mg*e^byq`J=DgMH{5W7qo6vB&)vYQ6oN{iSlS6a7f+Ouxzg-QH&ZjW-C_ z*}d&Pc3-=n`KkS@d4=%FAGI9p=oX-H?Qwtd=+>eYq{R$&E8y5>2$7o3_*}x6h)$x; zyO=a)Mm&HpeukDa))~+W#_R;GWXws>mY@}`Nil}j4Aw%>4hk;j0%%(X@9zuLL>H`4 z5a^A&V0KWTe!7@Lp|t)2X~dk7K#M?YO)w(~MYIU5g23Dwtpsr~tDy92e*mj>XuXGv zNs*#{jCm{c5XQU-+MmH(zYt*t<9LB_JcHp6)_O7Y9pXqX$3W>g;v_J@V-%EPB*Zm< z;!(s6fTAeGeSppv~=phFl*m_w!YI4&{_J&utxpvME) zRgyhVWF*DXD6K%=0i|Cq0`ei~$&94*3}@tPP>QYq`8Je(9SO(}p{FwPCn$a&iNNX; z;jSb;9iQp` z`fePf&4bdlfOakP90u#Kg_yu-k3i35v=^Zh8SQoGc?|X-5@Hgg{Q^Co(e^^-(A4yKnD9oZC^8=*@Wi|kE#0OnIrDsO<_jS~3X9~bim=o5@d$C7^llYI6`#*HQ~ z!)IVoIV@+eGowJC#Kk0kCL2A2b9X{lFxFV;N=Ba#UB#H|p=3v3()Vi^i^}0y#{33K z_W)SkAOxiwm^VV{-@qCMeSxtk&FdKJWGLMOAhssN2Cxa3fP9WHz)K!F^kokPeZ`|6 z^i_{R(ANO@>6zdSK;{1*@U{n?_YPxU4}F)BZK3ah4^V#xKtE)#?pC;S_FM2h`RzxH zc{}uDMjj6R1bl{LsGL7%uq(JgZ_h>l6uOPUOsNpt87%|4gTemX2+cE^gnq?fJp}^n zj7E7Uy94Z>jgUX16+m|}+Cb>H;Cmc19{K~LjfMUQcH?{Um!BDp{EB=UU_F2kchtK8bT5Nle1-Uf(J4Lq7;_Pn&H=g)O8ME3&(}h$8GRnKhQThZLPQv&5_*6^ ztkazsBohcfKH~#{_@|H>Lr-rBpK$pb3SS`v>?utKaZ({I#(hrM_zVy`#ft$9JvZpN zLLi>%{vw~Gv=7B+x;H?K)%^uNNj5kZpXnX|aaTdlA@^AXf0m6I?Gz}b71&ooDIEau zSs|M;)J{v3AAy*yz-nz5YO`eyqfLb7GSq$}lH!8cuKUb$pL-}@*__cPL0d4?uFHHz zJ0A-FAh3Q^$U+8jVSyE`F5+fr5rbHnq zU=Yg|vLi$F8^4I*;?7HV#%Ewt`nxdNCTLd=l)dc6XsCELgPr_@3^UrN&_g|_Ob%l-YNrnOAbT9a zXsA!rez}lsj$|~{EqRm&+39FTN72ZE9+X$g577RGQhgxk*ddHgb{Oh$4D?t=Zvs8e zgRXZxqh~@-@R$xgkL0s&44uj7&qFWr_yju3V>k3-Mt>hl`6PY@WFw%H zuaaGeU%(tj{}4)L53mC@mp95>3*$4DF`#jRyn?am*tv{$DD+CkCOgbyFgqk9*%Y|_ z`>XL8xZ{WU_zW<2B;+*=^~dG4jN8}04xfQd*SMb1$QP+R0A`znypf?krX;%q`%x&_ z8elF;NU|@$&RqiU1i4^-O2}IoLFe7Z=+)2#3}&wcBF`?=*O0d})UT0?JaVCTFxneX zItOTEPx3{esLtKZDDu&JJi0;u&1f~ydp*cU7JD24y^qoT(EAyij(vbp78C48@nV~VEe1*}@gucqyRQ}`>LAc4 zox%EKA$KrZ0Qx0EaSr(vqml1@&0rrzA-`cTUn}HJMo>Db%m98jMaXYG==$F=8rkxD zkHOF%7(EO6qsI{FPmG=o-Obo^-=7&pW%>)FQJMeBV4hgWJ&ZU5`Wu6}V!O(b7?qk%4P^ue5NAMS;OX%N>ARF#yG)iYRLop#l>Rq%TG~#hK^ZtaLkB_WzZbq|!CW-t_o&`giXQ1~lBF$s;%C#C?!i1(pLs|)I@=3{6qhZzS3 zb(hNCE&rj=3mQl zX4J>fkjHRn2`I(6sApOkL;j+bGx|Hw3I;m_3$4}yH zD73Z=^#QeZj81uN&rp9*>%i!g?~V-h4Yf{;PB!SwPib`qm5gbwqdG@T5{r|9pe zFpA1?1fw2;p30~t(9^)_h%He5&R`76$4JJY{EhOU@;%ezI_PLdQF)DF?3=+Tea>eT7I8p>Psk}l@l=I#(Wq06r=hwQQE1T zfNBX{#n_a_)r`3vN@)e=m(XXyT6`w|d5*DbpwBZV)x{SWdp~p?qsT|rGm89Z1EZ); zZDdqW=qARdyu1irLfKLJshoiQ4)hfU@2m^zZxPmo(AOAs5|n%qs8-PbFxX8*(7Xtt z+Ctx8oNu6SGK$LXEk^Z$Qkemp(n$9P_8(A6AF%gA$!~!A7nITp6xELp7>8{5A)~UP zTNrgY^dm-%g?`LflcAr0Pm!++pj2kSqI&unW09RdXRLA1FBpY-t5LlLirNFZH&E@M zI~bGFLS+d|x*qu!FzN4KGpYxa>;e?ocqgOCuDcjTw)~c%eO$Ef7)9mqJ@^Ud(s{cX z+D}OPnNcI4zkmZc2FL1xu`Yp1#zMW&HO9IEih~FX`O*!>x)f?M>LMuaL0FeTZN{1h zRg5(U>M$1UPqA;px(e!NEciCX&iLXyHMD?HH$e*-wGtX+ z6xstl1WIt7L!hOMbv?8Ulq0`4LMs>xKCD+V)-}+Uj5QzHic!O%tr;7>Lt_cTejD1B zvA>1F|Md2_{m*<5}_5$i~#eMff>3m?Lt)VfDJ_^re9eO6C zHbO@;_IBtPFb&6$4_*kS<2n0%)3cv&sQfQxobREOZqzLYK0)Id!lbgJdIe~H zh30w*>m2Aj#=<=)PD%*)8O3)9ll*5sBW6SC-$0Pf>AFCC0wrGravYR=5l{?(Vx}(N zKyPA<^PyChK!l*TFoJA$E4U481q&EKd0NQOIGe_Mgjfb$oPoa-96fe=AV2oB!ssq3{5=wOg7{j2He_%|8Qh5M_%H%1=pgcYe zR^t1^psT=Yd_EDn20V+;=Rwyp>KN#Aj6weLJfo;gUtrWZ&~=Pa1zqpa7Pp^SV@!d*&KTE0-|(Pz=uO7B9{LtzP+7grIF#Ra z82dBmyNp6UsV_@0a{VvxA!Cp)Y+)4TJHhQ$^+O`CR-UrcBShAdmfa04ya5h z`3} zKR{5K{>{)_pT3`QIzp=%gVJ0B4&dBQuscpOB>sakGBl5JDB32%Mwu8m$fXEsG88*E zEMVi@T~Niy45$Nqpc(LcG=&Bj8)a+Iae(Icj7E(82DGsUU84yjkbVR9B`EC%T?>e# zpjnLl5;WU`@|42}DWLP`>j$kWZt4p}A9|kP**8 zgN&R4B^#B%ZZAViK^Y)hkWGQT8Cu~%_NeqA8^CuBl&71gcF+!h(n9$M6uUC0%(?)o z!(Ba|hjwFV+-Y?8cmYazBO9*=lsBM`fcEx49X0xRybA5>LFL}hgUaO)hMvtve~)*e zVMZrCl%aSBwKXp9K@VqiDkHi#@je*9xNXys_)Kg8RIbFw;An6R*a`-MK>&5k80_&m zbO<;Wd<~8RxW85fJ%OQdC$)EkB3qv1@f~y+qfddJ42I*}4bW2<8iyDoz^UL%a2iA7 zTH|y^QCiMm94eEMjNA5&!e``Wat>ieiM3@#}4S(jD0n9ETeLv;~0Al zbUfqy1U-kbpM_3foZZlK8G99UBIEo3J&&v+&lxzg-2cZ`* z>I&#o4{8IZG1R{^F7zmaPWPbn%wX&V(3y;)^1g_%mqBMS)YmgEX6*Z*moSRbGMlmQ zhhEAkO4l65egJwIqv-xsjD0)waz+h>QXYVM4my`nuRyP46s2VzqfF>kjG|+yY_36D zO8LE(kyMAS^VkTzo^g6YZ}8X-y^)c0{eLmke>HAm^a%83kG0TS7@PinE2BC`nIoss;23#-{6g zDDwFy8GAXDdjiV-XjEk!Gr1}l^L+Bpz9fXH*^D|E{1MoY`VrK#@PdX(ZhmL zSrYUel^?L@L#fP&*TAa`t&ep7P2ar@s2v1?Z2TXOcc8B`_7%`K82cCKn~bc4QW+C; zEL{swEWx1jf#?K%m!UZsgX|7OXXyJ3&CeJgFd_`4@&Gg^<32OC;CRZ*M~p`4`Picb zO636*+4)n(raW$C6#3m}j7_%xoS_|Y-RI30uoacnHpZbmZucmKl0OkYfiD@^8~PQa z20*`NWFP1^j5-{;laUudcQG{fHNIu!H0XDXnh5=#krzVA|A0CVO1^^oJH4RfAAtJQ z2H6@=AKajH1L|KJzcSPxH^^52^|cMkGoZe?@jGK*4&BSxbiF^oKHQ7)@+V_c{rHP< zD7}9()YmljdyoyQ8Tmf6#)HZ>0&uL;4`a>_4EbE6{)~xwR|tC%Ch}Ey6k{SUg-3&9 z@ICyla0p|!AD#Ph(7!dEpqwM0yL) zVk`@KHW-U>v;)1HF=4~P#fdqapu6gAw%EWV9ooNE0FGv(0Gmy`W+QY!F0R37W48`WS(H2mK7qF9idPfGvWs zJE5HlZNvyVrZJV*5bAps-ASOZc7@FG& zHe%mZbI@pt; zHLby348^H~y%|Y%?Zar>p?w)iHtxqz3_ExTBguaK8SQsyn2}`5Lm6!^l>8n@vMc#I z(Efmue*;N*9l%h$Id~)^Dc?si6n73D%}BDrF$~3_g98~!_87!aOgcE2ktaciFchy1 z4rSyp=&=mNZiB}$@?_}o48?JSCoq!g*NF_pbc1wVAZJ49T7Y7dK{_AEi=cE4pqOQl z>LHL`<%|mtDt8vI^A<5Bd>;zVsyIynT)&{I-1cZLdP)j z7U)@wPIdNdhSu2y$1*zA*>Q}#4LY9Dsm`9m$OX^|j81j-Tt?mlrTYPjQ3mNAK>iy_ z*9CeNl&%Hjy-+$I=$AvOo&&iUdI6(f0iDXoHPC4c#R!8JGV)pIbcW)E!5NHP3#GCF z6gv!3c>wtwbQVK##NfpYtw#%9!ca^xIGd5LKrdw|{ui9X$XB71PoPuXr@R6AA1LJq zP`oclc>uJ&EI5~;*k6$R0LV9>RQ7=4h(RiEKlpnR==F?z7kUGuuYlgj$gR+SF%%07-o(gXq2xn=;%Y(i8zA>U$yWfy*n;FAK>h|L zp8)#D(1i@mzXoq-bjtT4hT@t*DnCGR$Kag|trH4TIRTCGO=SZ#vImt1aHy=vp8&1# z43ZB4yfH0;8zZXT&t<3k93dmESRF{BGwt9<^BcN|HI@yZq50Ixq-(_gNF8CfJPlLYC z=q1n(7E1x|Pu@pr0}FOz7tf%?Ad* zVB~1%Hb!p=-Ok7{&>alT69&Iz&w8=yZhG{+MBk&zprKQa3K(A|vO1pS$zd6?iYjC>LLD?@WK!99#5d;G@e zbg$nTNw(R`(7Z+P4@Q!m_AxY%5&V;pWV62*o$mEFIDj?;K3ODyjWG=DQ=~v+d{GK* z0&?*EU}!T?i0?0j76X(C#T|=K21On4J?d>yN6-V`qplV81pVVbCpl1SH!&nSG2TaEG>6j^u(HTnV z0L~Rq)N_Jjt3{~Wgfkb4`b<#lvuHZwTnU}QP&}__CgaS5Uc}Ixa1s3tI9EY0W+;YL zbP3~J4JG>miU}2w?Ev;>5Jgo0f2Wvu5&ipLLJ0bN6Sy7UQ(hN=d-47AP|D{c_#SO= z5nXE;^7}J%Iaq=3`#@KL)%YBSt^sRtZX4)xjOYNRYXL#WzX0Gn&NWc@O3_O=7d9<= z1-y#S=R#itn{hnZ>OTPOjCdA`Hii&5rwDBf;oJyCJ3}~>kGC1;7U(PZJL8QnZb6?tpFwU*g<_P&x-VcR|UA;5!r(Eh3-T zh0k>Sw*YljlA@jx5@l2L6M!w9yPL_P`46xUpJCskKN&d*`WGW7L;q&<-O&Av zPWP{7bevyQ!#Inf5yrV+h+<^5*pD$d(qG(!QDJB%qYi^MWz1pFEXF(unhkO=Ha{7f z%Q(1iaUP>!>*8jhIgUX&7Pnx`snC2z!EcKTKoG|uFU3WSf}a;7am68g|2VXSF;9V( zGUjQ}GDgug%Ng@BXa!@=fmSl=P-q9nx((V9bVA-oLOX-5xYsmjH_!v0XF_{2Hp;NL z7wC=euY>ks)YZ_wjETBa+>bGFT=5}{ngQ+4s1KlIUtpumiz!XOhJO^3t%3b9^l-*J z6?z0?wT2F0OuF8Yj7irx3LK5JA>GBtFec?=Amcm;9mJR$po75>9Dh1=DC0Z?J(f}P zp~nG~t@AK+IAc;?s0?5;XDM_PV=aJAVNCJ?w0VR{K5!wJh0pN$;)}s-eBJ{^eJe)! znDE zI%G#+3g{yY#r%qwFs2XsC_{0+;>Q?ML8%S^?JVdM48;+PsSE(c2#cQt%W!?%pm;gs ztb#tpP)xD-X~vY$XTS=a8-bEtfJt_vb5_Gvpm+^KamwOn8H!yNuVswiq0cc6{r!2y zbf7OV6#Fb*$C!TTdd6%7-3mU#z36(MgD>F!WGnI&;L!DcVH{j91lxs>b_;e0A+3b< z02Fy9tT9lFu|`9Y4#Gm2gt8cQDm0g|P==vA#)LgWK}KE!MR^l)KC~F%<|b?uDq&36 zE>y}mUqLGvoj$h)ZE)X*pzRrJ3A8gK;M1W#j0ro3hA`HdP}D)ffz3iEGS*2@N;j~E zLr-HY`tEebB0Yn#=8cOp>r5@2Xq3X?tz}ms9T{E8MPRC9;0r9PGZy|==qFV2%XHRe?!sE z5Na9}Z49B_g-&G@d?rMH0}5pjx{y&+Ces;(Iux40sK=o*8I=RQh*6ZMS&X7Gx|mUU z&`TKg4s1VJi~t0Gf=t~ z+5(IGVL5mTpCeHE8?b7iD;QKA5n9Pu)zGy7_qX;!DXqYwyuZg-xK`)`#(Ea|1=xl< zs6)R7-{7-^?qaMTq2DvsPgv@vG3JX2xe1E$Bg|KzM>8h;wgmo5n6O<5{IdjpX~N#6NH<~q3`Kef>lbJ<#@Y=nXRJNY z3Q&n-{)FN_gtZS^#aO>WuV$>@pw}_h@6fjxYd`dD#`;@`vQkDZgCdP(s1J4-bQQpH zwgZI?2^+_jF9!GHzFVOvXTlr_eVVb}g`%7Z>q986Ls;)a;Wvc!5fo)jxZk79%cU&d$PevdLJ2iA7ztBmy}^goQb8TvY7 zeF{Zh3F{N+PR1Mo{h6^*HZZ@7^$nE%ZQ=OupnVzZYv^dk!Zj+!GS)6A>_%803sITH zSSXXqJjTMkD^F&u9niBG>sx3Qn9t=>iS!W88&K2}!a^BT{=rzwp!*nW4HUjYSWBUQ zF&4_I5@kV+3w5ha0b}EDZD%uP1bP`Gai4Yv80SqP+Baj&K0-Wv zFXJGOkKh=>K^`A%&p0UG?Siqsf}(D2_v4G(p#jhapJChWZNWhFC5oYg7<$KI`(Q>} z0Ug3<@S*KP8ErlESVn_?Y(Ea1j_be&wx7Z1M?&GdgihBR#po!9?PoIjDNvN@_6a!V zROq>kJ`Fk%RKfrAp|^sC_h<A!S*K^ z3qG|S@XO60~9tQ6n%zY63Pd)frC2oJ(T_p>@85( zkx+E)0Hd;?uqmOMKpQcNv@xSjgwo%DdJ&q*s8LYV5kjF1cF?teqHAX}it?1hsOzA) zjJ*e%$0$lyGe*%pn=>|ien$(&{v3+(ChYal0>=IV8e|lGU&PqIL5mq1e!e5bDA;93 z38N_OrHmR0En^hfqnuHjpcRY_zu8gA*xR5j85@4EqZOmxg0^PVI%pfv18Hst?Fstf z^C3_I_q4%|QD6){_lBOun4dz=X6#>u*bO@p<`qzl5h&N)@C`!#2!(GD8tlCrenIGO z3h~E_jCGL^5!fVxv^eiV4=~QJLjKypMVL=RJAz}7o+F_66&xVUXQB8#As|frLhGOS zrAr{J$Dt><2y3~(A7yDxm2ZNtO6bBCe$iNDiF*evFFo#*aA(wBX=5|X@mLlsP0cXJ z3(YU_+mrhEM$&QMPf9Hwf{VyeJ}KpWa%g_In^5?hrJV8goQ*Ri&F1roLLNIJ+`37V zKtL&4!`mdQNmkR$K;uB;Mj6=E(eHB%{2V%382l_rW@9U_jm!*X)}quo@{hI2)KqCA zBCs`b+W*4v;&52)4~73A=jGuNpERT#ENEYUL&q-w%5g(96ykU>G!y@8V*2yjw35>O zC77&`UaDn9I%+R!?X^vjj*Dsv&(~CS6D9r>@_lTij!JFt^?eiE>*tA5F?l85V$kwD z7#P2N%XjkGj*cxsM=NPr=M71`Z+^HKU%G#nmN_AMLgFDK!p)jD%Lo(|HY;sjnw1&I z%gA$6>rY9oEUD<$wR@M&xjETZDB*LuTtq%PdG_p+htHa&Wq2Rr`O+Sqb>grYGlrcw zE2=0+uHAcd z&dm$TE{Dh-d5xr9Qc>AnmX=s))BgQWFU%i$Uc1smD|+|EUbD~7Kk3}op>WHt{S)%= zZ+-n6vR6^_aye{(y^cSgkeH7fyxPzs+%?%NY?P5X3WNXTf(iu4VYA)zD}O;!`gIw~ zN|(~>BX7%VK1@i}o$}R4uedG$r)kIv$#iMB`2%@vTY5*T-<+o^_(AYFeX>76*8W;)J)9cP3H=EHs?t4w1 z9r^m|q)bw4s-5!4%o$6T%$WIzcBc$Qwnfy@UAr73kM7d-7|LhOCe_RrFXQgwiEthj zb8Cs$`=n2Fm5$FC+(rsHh|9f8K-&I>%YBIO`JB-(ku%Z3)2&m~1=5@ybAhCDMubD< zW#kW?I+S%U?_S$08Xcrpz+);ay7w>}$vmq|=kC-D9-O1-$IW{tui1nvWxtUZN$sqX zF4=9%r#9d{zu$k&Wf%Y6XIk5FN1vT(f0k1jYv1`<*;sT7cV<6Adx%big}y zC^SO#95f;vzz?DgeJNzVqf`p8E7$v_W)lt(7@ zZRiPt62VjCzj`+4c|Afocs8VUq>Y<5%ZZV5BQHiK#>>Gt0ndiW(Q*x*4bk>7>eJLd z&X4&O_0Zb*$-7m8cV{>O9lNrR_Td*zlY0R9;a0Ju@mKdisq8xXY(<3ynRxDTbDEda zBtJ7h-hpV8(t)6=lGvg))P18Ww7TvdoQXPB{a&>97S-6Z62n0r3FM25W7f!_XQh*B z4Lk{93p_AWO;Hg)RNs!Nty>aE})W@SrmYgpdqr^ui+}TS_tAoy77d?H4FET&!n!M@5 zja%fEv*Jm_0}b6i?)t^r*@a#^H$7GY)Xr5pO0#TfTT^2#gKeknth9_C8Lxtv@JVy% zh;Sxa>E?mvB~8oScEslo#=0pq%8?aiT{;_iA-BVwm(KK&Tq*tEd?f=i@{v4k;-ph$ zLgrpy_1$-?jy!MDfOxwx-OXE`7#+)70rFN9%@Wx?P5&c5T!?8x^i=8`2Q|qEBy>qs zJzbJ?xv)phpFeWc{P_uKzw@#YBj(H*G2$}UZ)#){OB>=W*p>^ITAL40sF1SWTL zGSM1ZR#wLLO?|A$=%B2$q@qVQJzA(ISjX6=`i^On?!0r-v^y5}>(Hui-&P&^#gqHz z-DBp@A2Z>~tH!tQ)vJB$o(Ny?vt~m=x_gGZrKCEc-;(rb`Jb9{-)(VIMt$6`TtCPc z{ldNTV`B?Eo$>1#Xg2HVR%B*p*7b<4JVaJ@uPE(NY}mP$ovxf8ioBG2?x|8vJJ~P& zGEd5@j*Ht+TfV~VfD3HAsp_;d&=AJji?`jhHWj&IVc3@`EhqZ4qd`PxJR=-sI#qr* z^kL2M*&6jjoGC_mQ3wq6v$6t#tlTUN;sZ?sO<@QcSp-ssOpfd# zLzQ-U9b>+l1M=3LazW(H_qS-%zLx_dFC-XXw(JqvykyC8IWuxiLjGS%%ztC_qoVn5 zL;0r>k#cNRC13?*&avD9MLheki<8`x<~MwjTaW0>y7S*Oeg3m3_aRxHRhrsIcDpY!v?ak6kw;e;=2Jxn} z74b4e9=3k5BeGRqt9|-;)`O!5`d;61!Qlyou_Us2*)rK<{zB)l_Qm(jJa5pk2tCKk zq}VHymg2ndgly^e6-ng;rLEwfZuyw#Mf(OKqxbgya{S5t z$BgJ)QhC$Z;a$d_-La@LA@_?TZ^9M3$dkGpe|6ioy((G`ncuq25uE=TwCTQR|GQXR zS35e>9&J$l(U~CvG2GF9bYv$gjBVSvff_n1ZJ;LUXgEV|4&0$dPH}E=LhL5BA&SLr z8tT0z#Bpj~ul*p71$6eRgy%@KFZYe>2hByHxH{IOpx%2&^cEbSVIw=7?lkoOmET(jmz5-0DQNE; z{}d`!<(Tw*asvc0M}Q0X#vsO%>Ic1Wc3_^{SpLIv;S!}Kp<;MMi07C zJacouVZ+b4(dyH_V8QukA2Yy-`iuV>&t@IOnDD548Sq!YZsk-@5P>iTw~+pU@eKO~ zjEBg5KsaUq?PaR*VqHfuw0+ywElWd1h4tjwi=Mb$+^oE8%WE+65Z0=v!Q3jdWaRg+ zzKr}XGtZtieSFrkE6*Nu&KZ3x`p75b732F(nAo?hSAwfNb?fTYxAqx6On%pG%)<8V z4)4<8tVQkGVf5kF8Mobx#*oUzm9h4MMpsn%>yILFXpj0NZaA zQH%2Y@|cxsHx`L8COsjFny%5P6>oaAR@13R<4(WrjQl(rip3IiKf-WY;A%?W8ZU9-M>;w9qwIQDK?bd zy5t09p4~oeBCEgpUA7L-IP1!UWF9~FlB4IgyR_r;sDqKMFy$YLXPa{{Cow$iG>1)% zxJ~n->9lN9PfBU*FP>5P@03@ob_=C*(dypv+S!wP_R$0L zB5P&qgyerT<)T*(JE{L|qtA&Pku6tXDFgCx0ILaN<7&#s+2M@lxlMITX8AP3qDeCH z<{+Lo2Hd$SnkPHOPih^&S>wYfXa1(mxg3;A3Cy4ztXwJwv(8NI&)zFr514VzReDFt zRr`co#R}!gV!6u86IOU6pYc5XjBX&2Qc;R&Ksp<%Y>#lv|H(vRf7+y=lvBtekhw^h4W7S-e=bj;u|{;s;uilE-tyffji=$Riq|SQd*^tt_ULTkRvf9J zdwDz^@2KQyO|v~}IaQ>hy0f-_)9CrX$#(sYy;HW2tcho=pu@BC=3a9A>^7skT#-M% zMfq}uxov7lw2_T?kwLQz3|5;Vyn;xFSy@5tfiW0EqJjD0+JAERPb2u$3S7%FZHpfK zG;)gdntV=DTQ_KVdw6>5x{gMNx$_Voo|*9`hwDbeXYk11eF9b1b$MJFmUqNibRRqb zFj8$=Qc}_e|1U48aGy|ql{}Lpul@f}pT(DNqSZRgTe z|43==a)~^%qqOhz6B1(VsU>sl zItvM(r4Ci?xk%TC6Q?0ljPyU7hK%JKj_I!aa{FPio=su8ncakZc>S=nM>W_F`$v<% z$$hX)F+LnA(%9?#3=w zGOMywFSol`t9EJ?Scn$+@Q29XKRmsN*QVv16KV0-aZRe_wwA{`MorJZUXt(Ew9-C` zv{Ry=JoN97)?{DxU$bW?)C0U9LjFK@Zqx)1jSLu;I_UcUu!CNwoy+Tf&M&!JUK)8W zZs*%7d}3Izp-Jt3z9tcQtvMyupKT$^#OiRMxQHIH z=n%PmJs&zQEiu)EdAit?s!zpxH#CE1j={o3vzIv(@p@`$TG8>Cij4Js>pr3OvE1f( zXCz*n0_^$u8G-z={IcvU%;~hqXyJ8UlD(A{0%Uf{%tnB@OHLzM|B&(KNsA(ba*BNg zht!Nn89eT5d+~f(64`!&@u2PZElC(j*0$SnJdZ-DcB@RWn~L{3(wWUQbE4VmJf2G= z@lduJpO9j-?2JHRL6X5z2N9_Tt9SSsH&}J6S_9c+lar{WM=M411Q=F4c7d^!_o_Be8TnqBJ)~#y! zTp(o}mTJPBhKJK}L+-DtV_=?dt8_jH)-8y0Rk0TN^Um|)_@lh$3v&lC>kX1thUJ*S52K8p<=JvZ@&usEXd6l5qX4HXx(E^M6= zY-p6?-4 zMxeZPdFz&y#YIVNb4H5IQyZ3p2#UnLeZNW{9!c_bwWe0E#60j8**hL95xwP;JQ^>k z9WCi8eF4*pnAHBW6aO=!Ce#k~M87H!@K2>aLb~8dMndpp2#)lPX@uFxUOuWJG;7(5 z&KLeb)qisV3dHnl=mPGDpVl;cqq)>nP$%c$FXDv(k}rbkw}x`zhHd-~hC1Z7iH1N3 zXGFN5UE7k9u3b8HY}>nC@1$r>4AL7QajK z;o(;JQpYAR7J6N_ZrSk#Z*HX~YkQYQm2R35NV2d~|HB~N12=BL`13W%N)*SC3GelF zUTI+=eRQ@i5YGtBYLaV61mQSvyb>$vbk*Ej{3?@P@2ffBzS(EBrR%%%r@p6^aNm}^ zj5{{%(IwO*dO{k03=D5bQe@9in8ZYUXB1AtN%8-1aXeB6mfa zfxO6F^8H9Vy!DLlU^mMooD^AUG!2(G~YLP`M!tq-K&n0L}vWH zQRrg2i$xL(HJzd*gr6fLkqyH~iok_h&=Z}e&g>Lj*o^6H|dVNM=T$MJF~b`%K} z=C^2;-4y3F3EBR4&t(voZvyrX+?Z1dW=)LDK2b}MkgDdmz)I(i69I4XtMDdBZmH%tn*Gv zeGr5uu08^gfi?P28WNS zw-j9y&|}FOO?JXk`~TAnmL5z7_XT4ay#bb-N6fI}fr{O?(iV3aO0cZ5 ztg-?NR1-Y+U`r4UX2T_?``xy)ZV&!*BsHRjpdAu!iq*MFF)(~oJTDg2It#hMYb&YM z&-2}SGK1RIP&LG~OpZLe!Ma$}Qr}r`ecXTI{{2)Zb<86RCxrYIh;q?h%vhO4@3DC? zrV4ZyEFq`jKc<1V!f4ATZ4`D@cAxJ0C}dn8ue;UJJ&?LZ^&aUqhV9$7L`h=H)dG>< zB|GH!o43I;yaaE0qNFNBE7_0{w7o~3IJ{U!=1AFFX6lzT&@fs59z5fanVa{>U_Eux zwZ%cUr)c4$?P;iXdusn*wWm6_r!ikVGuE0?r#6_{Fk;XdO?4M)Li1w>(A=UXW0kM7 ziRCIyQy&xR|0G*d+<4$?dn0~ZyHIotVpG8KdTF0>7xxK- zw{yM3q2*30mHaTq4oS(PA(^!BrW4&I^>BA^>OZUt7pB5HdG!=#ab9=4qnDePNpB!v z4}peDR-XRjNm4E9c4qfx$21F94Xd}5S57!zcOpxq(P7GH6gRrIH`dJXe6pR`ysBNX zrqIjcLhDv5mztqra;D4B;S2M|`*cSy9x~DzasA}l!aF@DH#M@-b9-HQT11p8X zfy~@2wlKYwSC55JU1S#*S*%sFtx?ep8*2MfFc=I4L)p=c z7B-Yo3@Q#Xb3S*B(NK=Ghoj>Sw=KhaD}|y$T(hzW-cN2rn~8LB-&aMv)T~O$9d~m+ln2?FwsD1k0P|usP^$*9OYH(%XOJ;A~QJUVKZE1`V?) zwowV{lv~ErVVhRs5*`3_NXZiI4-_2-q|04??FWR7bKEF&y5nJuqF{MxuvJm3sB<** z&`9k5ADls|AMkp}4O-*jgidnQ9_%Yu!B=dz7e>C)38ijgZ7`mKG|WZ>C7wA907sEO z&^}5D4+cbAQqG}+(A~4#z+mk<^WQj1bTfj3v&;TZ%WxS zL}#~e-@a4(P8~bMnreDyqJb)#(p2jQ-=fuOuH`{BamkIgbDi7Z!H2Q_Ya`Zwg)p0X zV)(cWtoIUGQnRxWmME5*spIz+42{P4Ra8$cgd*`%EK(bfMo-YQ7F`HkcTGImM4+Og z6qgF2f0x~~(xDDsRt0)@)SZLx>A6kwLKT&gULohsUP+~2O1w)hGP9d%_T+7meQvkz z)M3X;bL^(-!pOkG2wY&kC{552-sk!m@7ru;&l;`a42_C{VkIkN26 zm1Fjo2fUtO^T+}12V;ds9mF+}JxH(=^U#j1iS<|UgIAg^s_+L*;LS^~O2+$0$QCy} z)L+eVUx=dqD&Bv$%ey0E#Rejt=(m=XY4hYN>aNP7$TsS(Mg}(fT+WdeUWi)uS9Rq0 z$ge*|=I+tnd~~B6Df`P~K9k+#`d@yH^sjjUFI0tOo5+We?U5gq*c<5{*@*6a*FWSm z*;5Xe!z6lPxIc}9*Wv!zcp{DFzFm=pK?nSGD!S>E2ps1QeBFe(VW?v72&1ZwLondY z!G4E1g*gSya&hhKP(>Go0dE!$bUEqB1sotRhcZKX2%XIX+qTh6L|&_DeeCR^QrjHQ zj9Ez|+IV)X8*VvcrX2V8&RDM8@u58x{SUHfftbEBN1ATOm7Xf)7^q7Fo0%!Jmj)gm z$p)vPK2~L7B_I9WH0EJRJYM}ud(en*^XzOXvJ3FSSM$8wco8{u6j4_6(NSMnWyYTu z>5EPOHU5-{=5@N8*CH{NdlVrozA)VOq*ey4Xas;;~x1PtrT-Y2m@TD%P7E~)j3cMgy@yvG-<8%4AeN4k_!tkf7y8J=hi{U<3- z?BmpzVp&{olJ|9qCmGM|rz6c{Y0eYahXXGU;d6i9su z)oqI9r8DcF(-6cXdrnD6Tb=f#z&}!LPBQ;d^6>c8>q}m6w!C(0`t<$rmE0Xc=*3Or z+E^M3#8KfRa;UZr#;z*{YA*VMN$sOsU5B_;8KwWvYNL$y`0Gow{4UT$B4g3txWY~tEA4Q_2%o()239{b^8yw20h4jG)W{^?vt&{j|(id1wPhl=P zyA8U9N~(Ddg2&NEe6kVG4A_3ph=%CbbqgCiNMR;0_W!5Nv!iqBiL=VwSF>7RJVyH`Q+&p|1Li#v z0yFXcl8H+0-YgIhf;T#j-PD(IQ`%1~;l|Z#y-j1|)4J*vZ|^X-&-njVI%pTq#B{8v zHN9Lb9dnSWY`kNS7f51po_1JOYuOl#V@wb&2X|gmCM}bo75DBc3A`hS2oIek5aEfQ zk@jG$uyyT8TghfNZWOa9ZG@ayjIriDAv=-W#MV5)wrdjFaJOxQeJ9}3^yb7BypAj8 zPyyw~#&I^LA=BiD7F~0W@Mrf(Yk^o4Qoo~_e7WV}kfk-S#JC(twT#zC-o zGu*$qxOC8RdL^$N2Fs3v-wJSqEgbvYI+7AId0RRj?sHIwlC?GgjhoolEWQmWQth@&nExk#x-dHFA8R}kgMDclkEBWedJ6+tA}`_{ z-C|ivZD|s_y$Knr84~N|#`B?GN$A@mA2pk-zHa*(6vM(NG{xW{A9FbcsQ5beU^20j zX*~%{n!Klk@~)jZhtg!mt2eaUW%6$Pu@XsbdZL?NCZurxn~8f}##6{o2`@gz{=X2{Z7#0ky4}g| zXtu&eXzofn_c^Upd^^ZQqsQuZ=jb`{V#3@~y(eKEjQg;_#+P|mY1SnExCo>+t4aG` zCg$+;+S^_x>N!NbuRpt?WZ~MM4!aZkC@}ho2@Z;<(t-c z(fZlggED#&Hl?(bH7?!Bw1fk{O+&vkfsh ztD9E4d6K8r*Ktc~uzXu%y*Tpc4W2*eC64`4oY3tS*Xro}mp!rpX?kQe8)7{&q=}yU z*k9L=SQDQ5*i{|_Fm!(vw)~_wzw7J3d#$MuWNQ2bPknvh2}$T*9-Mo z%09bZntjJ3&E=HlcvC>NTF|Ec>Dw;1ZJ=@c|FCX%IGDPvUZ}fnBi7f`_croEI|e_z zGE-tdQatc!vtYdNf=wwcUsZ<0@A~-cIRS-PLB|%D9rFSc-oBKHEg-#I=MEpcVV_p2 zN$&I(&=^fHNErjE+p+7j4?oL@8BjVTO^P}9jWywVF%%aQr$nG_Qt^pWr}@s z6Soc)`hnZs{^x5kJ2b{PjO;+IN?-i`h|j?mn^njW3^Ipy67gAaGa!4lmfD{gw*&sH zXct@Tyo`|@Y(nSP6(fkAm&Oj6P3pBn2*F@?C*cG;)U2z$gK*pq&L=5*2y?$-CiV~} ze+bD#!hVDXl^om{0YY#0VA`&dHk?%0trEM_T9z}_hi)R}iC!X!dPeeN(Q6_RZ={86 zy`z`FF2w)*3bdneED?MK`7};x z?=kS8@=4ikII*Z+s%w|wgaZ2@{XWCd{$8%auwdx0TJOZ7Fku<|AQvVNo zUjiprRqTDwU3#6Zr>AG1WP0{wlF2fenGi^5AOy(D7Ly>5g^-mcAiIhI35Wu2?8xFn z6hVXpk|2v9C<^N5hPx<8P*4y-At9N*^Zo0byPUi9Oi#e~-tYT;FAtNMzPC?RojP^u z)T#Pc6-MVs_&i=+Mu{;R+#wAyYyMwr8vjb;pS@aZs@J$|-V5eqSFtP2imTNh4E}81 zu6gFY+MV;Zndc6zy)hZr%|TZoko=*?6sG5I5EWxM|1jlF*mJlQ&;^R4Bl`;Z0D$Sl zjSof7!Ih=mxG49<(U}uP&!!^Dy{PUBJJlPjUo1`sWBk?+ ziQD#&y;OnJ;5+~nQA0Nf363u^g3`*B$&{etP<9uk)gm9L@P?`&pAcb)FM`){<+gbb z)L~vb;5ad~1-DjpJc^2YF_55$oIIjP(A~)kAK-cgte(Yy{dle|_@s^xeVibQN1E@y zF91vK6$Ng-2w?g~H>|p6i$FGYXEyS%1#YU+EJYvW^O;fB?m(gq7{c~U>GI7smt2^* zNL3VzY`OvFa&K}T(eXQ*TJb1JzKW9ztW(HPxEWOO=^@sI+?(f;{N7#*s>Y=`rbzfS zs&WbFMtv2&H>0fFk*<{J9_tWAb3-W$ezF^#CuFkulSFleqDT$_A&Gh@cF|)NxfJht z&cTXtX^wte0*KMDDWHod?m~&?24zz&SqB2S$ZcHBtV0-)9*v`})+XLj6?ZYn8e%<5 z?6ivAdeBR>lc~!2w9(>lMaCKY3)R@iZc;M-S`k0T=XhNDyhf}2I`5%^9wO*oG zmj_07LX;F1NeC?u7?&d5u&A zFhNvWpRsn%XG-}v^;2L<1+Z#tCei%Lua>k*B?IHE(e~h@@?v!?jWFkc!jmp5{VE(6 zxPy9_|}-6nFmC8KtN@=>SC(>ftrq6!%w@)_6aq_Q|MklPK&Q$f(Ui|6rdyCnlv0L)jHLaOYF)kI;kH5qn`{ZYSIv>#5bF`Vu z$frhJ(lBw~9fuFJUfg%L*(}oX2wd4w_9nPR)~yo312`asq)b>3Ez4z+z_3V_Co;*5 zEuFYWI%L@CdA0-vJO`m*(wf1wxCtvi7}d$ z=8m=$3O%XWG^4sdN|dI|e;ST5=A}x{nus#)g|GeHe1VPm_iuLV=8@X6UFKP49vR^C z|7Egmtn5{G!9N>~4WGNyybHPDPniGp(zn?h(=`8Oe)Ok9?AL5I@I!vH_W|?SA7X4y zb8I|l?oyD4iJ)a%*C>p~Oc26lIHp|rO zZyqxL>Se*@@}`@64vlUNUBEwXIiu(MUoYdJ+JXC#xV zjy`N2-u?Tp3^#e7*}HB&oBXY{E1M1)v;GcdY)E|OwqfV-(3#iXQfuC)Mdw{?{tAwO z&_Tp_no)ap-S!r+N5??QitpoB>%4syh2g0|wyT@G?`N2TqPrV(A-08HHjus*cr)xJ zWX)%@ijp0d?drsBkE`M(#i+Ow=_yXv^T)eal||{jwotmm)zoj`81(6(uwM zo6jPCxuI;Bj^NtYeTO>BLNigOFpRB)s30QTA7&@COmIcT zk+g?hhyj>J0sKHNLdr499(N2`^?5@e5YwZJV%4PE&n2g02ln<Pqd4uca9OB0DMC$mFXIqr^0rNY)r^dTP8D236MP*AEQnfGOm?4+$C z1@x4g&sCI|j4C(7*_PGSn7=kLU*HEBEipt^hnvCu=L+dT#c^exH8{1X`O>fdE2Xn4!ryxV zzEg{G6^|;Tv{^wBS_73h<8PeFDb(DFvdM_1B4ft_kf9WRa-iU5=(@5p8bd(#OsYrg z9_Gnt6lo#!I2D!PX{>gpjG^9iRt^lVom*H7)s-0BUsO0Uh-mgKJCxbZ)`+ezHeWL5 z{~Mv6o#Zio{9QEE>H0hT%I8`3t>5pBm^p2u`J1}rTJzOL*WS|*h)mK1p-!Iaqr-1dXc-OXR440!b}*yd;GYrPV%SIaH~;pg`1QNjy}Y8YhlUQ zZfR`RN{jKwhY``{?;K>_#)q#6*8n%R`M4dYI**T7?eeqmBFmPb^YZ?%|#pfw;;h zcDIp;D%)Bs61);S;LrM`=JjZ!KQiZZzj|kw(#W#AW@zI)HuYO;PT@YSv7z*;n*%E3 zif;C->u$9iTFj=fhcCdNZNbc%LOO{yh=i6JkAaCFqWHmDZ~Y}G!im$ZJR;P-mB-UD ze9JFsEkEHU&nW(FXGtc-l59XvUi7BiLf?QQpuphnxc)bBS)OfeWlC#zE0njRJ-Dt* zi(A(kuF(5fmSskSTOF zA%j@r%m^)pK6+acXOS=G4k-FPXF5tO+qx_V^#YXac*<4V|H<&U=cAaBxZgz|d0rCJ zeE#_hh|RR%O!LdlU>S*m~Z`h(G@iI&h3E{QT#w`eN^%@vgIH#ADK)K|e@)MlTFE zHWlm`K_5+3W6Q~;$KY{eHdaNw8wT0@VqUeSxysoskpCy%q;FT$NDK)W!QuVyePDZq zQ@c5e$uQTshntb7ByqEQvT^tFt6`TqlzT?A9!Tk9%c1|6f&z9axD>))vcMa# z6mnxVgHuNqTB4C3(D?mJ^iqBg+=;{-IE)(8*Q(fx)+|61UyknyJ8fS`2Wo`bR_#po zfO+)b=VQv?gW7^Uinb7UFu3~wW}f)o2IVZC?XOWlgJAF4X&7{`vxdR(L|YN(V|goh zTI}a6_#{D<^tc8G9r)S4B>54mc3hgxXMmUBC= zgQMcP&6W(;@$iEnSujUHsVzJWkRGNqKq2jQ-iJnC*X#15ZgKroc|KxpMT&JV<>3SO z7nmB(hg1J&(Z-8pPUG15XxL{d1o+_mf(4$7{l55cz+kqyeEiF`pfVeY&0brH`!(=! zKIWrFS#2S{SVEOSq=kHA4)J4M@MuA~rHJ+uI3!b9=4h%FT+-R2&h?tBNwFVDP7ke( zC#|(nL)aKL$ugbqt9C-7W=9A$Qtbr4PlpUQav8QNXNwwd#}IgHyaj;?N02R^dvuR1 z?T1FVKlkM~Q_~3>Z96OdTT+eB1WURlIX{2laf{D8Z}D*#+JAE&b6ftOmb~kfuZa7L zF5HJ}D5?fhr)=^B*$d#^M^|A1Z{$#&9Gsj#uSIb0H_{8aj=;0HD~!gYYrOJd|MlTHXHuo*YUIdV4jWIo zyFj}siAOaf0TivIf%sAqw+TsgEohFcS%B7T6keiMoW5*ZCq~3C$XomDJ9q;?EV0Y) zp&h_nbobq^>SLjG8M z2J~rzugB&ENH33M$)D6H0M9V5RFbImAVq7+G2L}aUAny;7y7X%?(?nd zd|c)0t#p?^hS5zverpM``;(^mo5?4a*Bn*1{8oDt+kNYi-@IcDKwg)z=#iVW8?m7Y zJu)lfD3^rD%9T?D$ypGDyh^c`H80>MB#X|b77|7qmF|tPpTVy=%( z+rp)pWF2|PTqVyo>ll1ptK$3aj@Yj`jkPq_xEt4w!rbBxpRO`NtiACJ8}P5iS|nTW1^29(Z#D-V>JZq>=9pIaxD&U{3H2^q+ob70IJ%@T(rP?uN5!7l6 zdrnn(_*7YCMU*)R0TeH)Z}vUze-f)3!RP8sWkqR8G7*mfk5gCbSe*zyD=CiKBbch9 zg-tqHp~Rjxzhlpl<`E~Qt5TcN; zW#f(%8O_x+*|sw`RXB6Kw1%7yxPK@53{539EW;PsDiB5Hp}lyBO3CiXGfiWw%6&)` zxy;HPUnNE#>*jcS$lyxP$p}8dKa=PQT^9a~NXq|y$hA6yM~eJ!R~e=JZ&WqMSMarw zQBI)zZzMUu^ickHtt~C&=<+;dzBo9c&PV)LsAv#RaOZP0Dc_%SsmwoarF^?&dQ!e2 z)31Y8fiaO$j;EY&m)07a)`*IL(^RpJIjPPk7OCO^qO|BV@6%i=2d{F{yj?ooWMP_n zP5CNx=0i@N_wt=}=qlurT4$4rlxx-mqi?0?$8NnFc1z2kpsn}w+T+V_I zS*+R5(zLygyzQf19tX?(dD||J1@pGCUXpVhKF``&Uy}iw_VUgm&h2B-TIVi@!Dnm^ z-37m&Y1-$x%Jth*PcF>V1?aJv zn7TlEhfL`t{1#uEfI1ZzOuX#8R>T|pS$Au|=GxfOfH} zuqBDhoH-jCGH2tu3e6dP;+?b3LSjq^^Y6$}Oj&oK$A#3<&?_)wJ-Bxs)r5`sLU6uN z!`Idtn5#fJ#|Ui(HEFBQ|0pET zw$W%Nf8As7tqs@R!-9`ko0+@wj-u@0cakEmw8|bN`{0&6fbPdY_ouD8hcj|k$pck6 ze9-<^QH2VRxRehbV-+e$lsknA!^9h#g^P;w2o{m!hWbt-h1^h0S)N}SZ|3tz9fhCD zC7Z`W;b9~BHbXJ%vTbg)NJ2m^Z}C$PZhR zv)=IuuAao^wiS1T7O*8gp}D1ul8-A1ZEtIcyXzI3(@31`ysmsTztr-5MHlNI8}o*DN$|{J2ov zqeifv?b9ble=()xbxXp0@1lYZxL2h228t|o<;#$(@Wq8(|CFofYR_s3ByTZv2aoXb zLt4^PosG-8w2}14y_pj3%?J?#8A@8(z4Du~>J9fQZiMZP(kF2~kV2W5d|WPYYd~7+ zq_N}2b#{bk?MUMG*K|r}0ZH6@jxT&H@XFg!VdspXE8l>;UE4Wcd4syKH)}~{EFj-x zc~2{6N$iM8xOQHFC(10PE-djr&%3h7hkZF$_&rPL&g(v@ceJ+)MHj8vLzTT^BxUzi zmxi0$_wFiuVDU=-?vSceo$Rl5b`kpi=UmCSjyA|&wj;e|g=9aXvQP88>Jj}g@8$}> zW=lR?-CNq*N#TXqo5u!rxAGB$?k{qcPEV4`zo!;DNO?pad)Qz6%I%AFrV<^>e;_>c zhDN}hbOP?AgDLU;K5Gl44&+IktOB2w((H&&ak?UNG{asMHXcd|j#(whOtcPJ@GiM| zD3i<*U{x%$MCY>u9aFwvUqQF42aog>;v7)HN=v6M2&aTjafjjo&|NB)I@g&3B#G|} z`=(KR34h%>;0b$e35Iuu>zFcyG-}F>DKq@d%bo2%HG)=!o-8!Slafz?l9uheGI+=c z9tb@e=kHuDpALoYiL;^5y(dC+Z~DM+x<{V|b?<-?bgxu+Y+=(RDB#LF+&mBa4WxOq ziqkx0c*Q#e@&EU$cSV24@M_mWO4mEJpvsN3dxWYS!;W56T@&qL@?ln1xl@8F7j~{4 zR_UZeF5NdTweAM$l?S$y*`gyZS*$KwYb*;T*0P9+iDgJ7Chm!dp9AC#r0@&5z_V63 zO}xT4YwOG5B5~aBz)nH<2l6chRVYd4An7zH$xlA}dt?Nkou5C|Q*chyl)79sWz*wI zh&OD&|7wbs($E*0;le{b#au+Ct9Xd^G~u%2;Ji?l46~4+U&L=;do9BMGz%N8=l6O>B!HQx%UP^~vgY zQ(hWN$J2UL$MqGbu*9N}nkp5#ttTq`Srxn3yyJ#r&p-dz<8!DBffSLhwN964B>E(pA{I^59GS*8U#9NIB$R!R zpv|#5^bl}KpX1-jdpmKwANty;szoK{>8e>>MSVOrs<avq^eQ#68FBg=_j-Z$1) zR{?y#G#3A3%#zTsO~R12ym>arsR3cM&2mRTyuPsD39LMO7X8J_1&! zB(CQ3IUzR=^l7{u^Jxo;kj?-)E70I`td0salHE!H@g&^OYRByNccWVZefJ?&mjv&3 z4=u%aKZ*JT4T!8KqVh%MsbExZ+Yr&Qe6T>^X3z>kRg9yFSV8D^sBNJ~n7T#&2x(tj z2TF8Yts?1)L{E+EY2mip>HS0yyHiexBC;HSGw^&S)MyJZI5qVA8puua&NrIXY>+3M zK-db*_9RwlwzXr-MYD&W?Lx4c(A&Epw~wG6jMgvK0@24I$cmV4bcE?60jS@hJ_E$K zN*xB&q1*6vA4NTa2D}s?j#24B16uGIhPny2@)|R=4YI9j$ko%QD*Ar~KjdwqE6Pfe zj{e(|;02=PGjWGMVc#OlX%V0*2jrhpFXQX9S~)m(5VB7Js-!7ZP#A4KQi9n$mvc&T zRbhAeVkKjmD%Y!xYOV^lL4bksBj*z-{|JT5CmFpH0LzE%=P_dSBd8T1+mDTt@_}k- zWR3Dtyo;|PmjTcOYQ|vmU1F&et&)h8Y`sn7fMn~5y5Bh&TYMF{iDPPD9bwsk9KMdQ zQfOIirp2NL))8N2m_w|zqSuj=mq$c-nz3VOzo zpJIG-L9GfFu4KY(yV(gN)vF0Z^;H>BpUZ;se63vHp_?$~g^BCA9wQfr^mr`YdZVeG zb~YJu0%Guv-H>axpMbdueFRpW8N_ zEo;?5>%FTj?9`-)hPQ@*#qFsaB6|%8& z#kXZ_r(-Qi$o*RP_x$_$hkRxG{Sj*%D&o+tfs%|9IVm(Lra&X~`;#9H@h7MAh2sn_ zXbU)%&gWi#lT%14HI>(-6m&Q(Qa=!f6Jx3ha%}Vz*RfLN&>d(yj7hidq{@H_iUdQK zVoEq-%dOv%BrCWiOZ8GdUxr0ot8+-#Q|6*o*3||{Q@X{Vv5^Cyy8Oy`ftx*l)KPF+zRcG#p*i$`LzLas1%0 zT@HU+hqAbXQ<@@MZvWa*p>(BgvmUXCK;~V9GVf;M&ZiarZ6sQCRej+Fq9}~e=>*S zeLgl%$|3Q85)q$RIV59n^F=+J*JB5xEY9^Yv^|n0M5H0K_@6@iM@$-}89NI>Ol>=U z2RTvcgzt;RE07Vl>V==B>02=Om5ndlWeTQND}*VQ#bH$Sb96 z6~|yzV+`m9HUk7Zqy?RGKj4FlPsveumk$fnZz)71TJr4e;>uYgV9oNqNeV81Aln7&~ABY`%s+E_*NpUh#y#AyW z69gQhf_yv5L?IijMFWl_eNseBu$}vG_Sc2P1nrN8i3wVRe;_UzqgoLCWz2t40p21q zz?qG-NM|+Pn0Ku=0|zBKX@phmKdbdmm^ZPt`z~Q?Be$?q&0F?9XX0<8ACltV?O=1B zC+uE_w}uF*9ic-uU3A`njXa-|N~l|=U6FtnUc#8s&d zq(SL~{vJnwvq)f$qI*}oJW+}={3QD7g=d4*5x!^K-A zZVH86y-aCu6bQe*aKJ zF1(43C3nP7NT&|U9RNP;Ub^o?^3a;JM5GA)dMQ%7MMQ?^gWCk+gSXUrADT(= zX^BV*Q+w&rPDQxdwnvCpQ_sw^gpb#W1B2WTx6oeEkfTaWsS|FY2l-u2Xb)Uj+TNJ9 zL_7=ldOlV@F5%9s&Oc>{+_o{wqTKvS1d7o+RfN3G#h?cIQp#>j`T92c1!Vd%Rg~#i>Q0&w#BY{x9Z&kM#EvFO|E<|^jNXB#fh;?j7Xt& zFE855ZuPr1^oC@s5p&AT_V&0fr0OGdxs?U2S+RJDHYp_UrA_Q2HV%idd!oSf={RIrn{;fm%TEI)x z?snfgjNHY~AQ?gd=UrjgeB8~D>EKOL*5F3lJ&pvcEa72NB;WocCB%2Q=M%G^hl;(F zuv6urF}G)t^DJ6=bCa#+Iu3DEP_QB8n2M6c@0lkHyR)1%okDCe(O>`c!f8z?4=xP86IOM`Y&W76eY@lsvzO@4_)}{|3?O=kUHfLon(ICFidf|x zpxdxY7X2S0{nq}_ia3u)$kkJ|hyn*P6)0Nt^J~>D_W0kRO2su)saU`}nNqN5vCun+ zGY?aP=#%h>HHcYP;^t|=pr{Bwd!?^FQn(6{$6}(-IP>st>wZz5hc1X>oFi%^diXv} zxF1^cFwQSkOEv@(=2bgu8YcQ*K=t8pvoP7IuLz%Tc%a^KDA*rfSXk5qxWQ=c2x$W5 z1Si0bN)JB;Qgqt!6aHU1yKtZLf9xC>`75`BDdq6op>Dq_oJOI_;5X@F0B>??kTGDM5I@2wDuSF zjjiT0KmC^Ziw79HU%m3I-GIE@d)hPR3%6g!N`7?F6>K8D4Bv}!B5xv}vH~%YGq+dr z21gCeK1jWeEV{i*SjvPqmS>IzoR00f7U8=USipb{8 zcl=~K@v$)a*==(ErI(k;d?!?9@kDjl6~NY;nwx|a97pD@<>IRIT%sj4)Q zMWBQMX;RQRj3V=olpO4(guOuV=fd-cYdA}Ez1IB2-pBkE9tW2});L|(IMMet4)V^T z07*jDE&4bm6UfQKT_5;Fa*DQ@((ut=i?VOTjZcYEf*wUqgRNecMPoLm5%f=k`jQ&W z$26AToBzo#kzjwJcE#Yveb;Go&B6R5Tqn2X&$h;@P!7q>3LgzD zg{7sygsLb-+aQq139x%uIRqertObZ@A;eohCHb=*d|S>t)<_BPTrSF$l(C2*Xrhb~ z3i-5C!jB;U8@WKx- zj5y(*05`XK01rLVO}LqswDtvGS7VG&Ig?b4ICB+|3~^ZmvuPZ{ z4zeO0e>*l=sZ^$*!A}fxld34C1ZOf{NSskO^W8v4=?&$Q;)hD5vObkEkWt!;!FpML zTaDHciAo0Sj@#I|-}%h(7aeXkX$xS+zPb-1U!(ruEB$AiKb|oD2s5^Co7QOlEB_Y0 z&Z0rs&ZBqQN+Yf$2|vv-0#k(r<={`w2i&N-Xf-*gx~=39>>Pr`qOB{J2hWc)D4!PJ zB{FGOAX~y>ABHTI(v!7Tu@v-DI|He+xShw-+-4X*!@Zt?Tnp^2$V=1tO~36IAopSg z<+DSU5#+<-$^>|Icp6_Cb-onYtYjUuGxjxECV{@+G6ZA?Hd!?CFrxDVn;+TwwbC?i;>8?}WfI#EiKkyJDlkdxPHX=mg&IhKmE<7P@z z4#^!fCNh)_)MI3n!*V%Xrr-wx`*()N0f%r1Y515JzOs@CsLWJms?stU33s|AGBjbU zBvPi5o$^vslB4<%*vmR+9??A$5+;n8&Of5(eAbf8KTk?F5gG2?=Dkdi=&u3 zvVzFM)wRv;2vmW!R-oEJ(p)3UBFKkFMkLqCn9HPnH_NBgJV16fId?MdF`^yN?M~Rk zQl%r;T1uddFfFKz0THT-MTK?00!}&rP&27CrKhuu!DGzPz5)EL{lI)nJ7e!lfBF;K zYIMIbFMqaqi}tZuf1jn@H25O;8@kTQWA*9hI)^n_`H@x`+*Z!ad*3BLm6*_|hzSu{&euLJAors~~5bsvf%ew>wvGn`o&4;gZ%gqJyM2Xmq*?%7aFy4OEngN6`&z zy&$UavuFrb1AN-1hU#ohHeFttfW~q0OF9O~NTxxx z`qH(>J?~iw5#_yy%y(WxUg!eGmMn=T-Hn^uyg2Y|apRoLjVXyiM}W)3M4-vC`{{Hl zU17lahh@S>gBfbY-ieKJn_8_-t5v6|)#iz*{6EY;=l_v1PlVThhp}RhV)X6(r7?N$ zGbB@PQ{pGXHkKoE8#}W-5@F9;ew266ii(PKMY@uYP3Iy1*xXEpj;}p#*-bAQwkze( zaxQ3TP?qepNfDQP1&!|)askG&?aaa{te=g58FhX`AIKhvlxjTtlM&Ww1-cK|N_wX;%)-9c+C&EzP)Y00sng_qrOqtLE}x=*zK*prX!0JddrGmz0;4f@>05%uhIU z(nsPGPL;4rk93~g6czJRPPKB@`0}9S2pP-CjGP{5FCQGAim-g+*9+fLp1yZv9~1nG z9FcXoWGRXyI1P5#pb2p^#G?>yM(7OUp!$DzE^&)Ozc=(PFewPHc|Jsb3l~>aNg74t zWtPC=K7lzcXM9wDRt^!!w5w8p;6E^b$O7?E@?EEbJZ%1Ob~^>Nvec8|gdNuOIa?TJ zj@o{%~QH7|+rI@`$KOlD!J-`8ow(huZ*GlS zMR6QHZYd(mICG;=6-vKZR^|m<`yr1a6x!*x=cVLNva>GF&O4IR&W0XFuZ%Ckyw=l+ ztQPiKD}igP&gW-r?z6+g1wgitER3mhJjY&-`@07G-m^nag4)aSM+<5`7EEHq|P}=c;fhQ&66+ zD@ftfHHu8IdGj8ahz(nU6>+*3;2_hGOH+{!?^@uZ94y<})&@V8+*o&~L5v-ZILWc* z(D23^?zY43zutUnd;4*%ha9*5QfV5$`Sf2_Cz9;Y@{?9!FXH>THJ@X04Kdg#%Y7!G zeN}~rL(a2~_%cdx9p`x)dmyEq%RXu@Dj&Suyi#m!Vs701P3#Rge_P)Q4@&F}?R*Dy z1}G_^oc2l@N4QY#IEc%nePgN5SXPB(ZK;FU6l|L&azK~vu!a?4&GExAEkP85XrzLIw9d1P#o(Nob4%Z z77=(?UVK5V_0T$uv?y2BM}u()tz&6azKZ!7k&n5a+uv4Yr7JCh7i)+EJCSTK?Vi5r zYRJ(MgFk@PzTQI|0^D^#=o#Bo+gjHu^1?`L37`7NwhzZb2g1fjygDujVdJtA3nJNw zv+s`B!`5AThjLyHPCRbM*nCtjsz(W3Xt#6$-C*I2AuT3!+Hv(Ilw$bDmgQ3Q_4OU~ z9kz}nhSd=yS;;LjJj*DkFxo4gvw@@Wj`HlT3AE6_ntly)do%Z4``vk^BLdxq}d}{@QSBUA7;y++Ca>N4U2khH4lhsYQ-vZP@)ly@lR zX>Ad`td&?v-dt$@(|prhI5>#lZHiT}ZF%!3bJy#yo4Z)!>#ws$T`{+s<9F|2zeW^h z5&PBd-R5|6E1ScXptr~)=A-7_4Dda=pb52IqT;DnSuZk*T4jrZcybu2$ZN1I3!zPg zJ_8FguB)kW(!{RbalK+jh(o$UW5aM*$l?^k`QZ2cW~dlhL~86pF#B)J2V!%4&Jrhc z`Sej%D_E9}eds{5A(ZiHQ(Mr6yHYTVHr&H8Tks566uZAGbTTZ8?efs_5aRnb=<3Pv z_Q#?U4kxy<(j>Gr+fWberW)Yjpu!x9e#%^1cX?ZGuFpt@XzLi?MOtih4s@t}6{Ny7 z12xg=$_$r7C!5yE-~uTI>maSJVn2EMx=X$wbh+mDpL+TWmwZvE_^ZFy_ASjH@{un= zr3a5xzx|P0py8`7`_&cfioIN|FM0a1pIu>IxQ}c5CE6`}?;0$d%a$(F-kM8_{+_+= zeiB;^|E^6z?7JGZ83ZQ-7Z{5Y=sB87pfX?pa1K^BM_XsdI39y!*(%kcyG>0IFH`lJ zSxjD#(Z^Zk^>+*$J=_vC?d0oaYgMfY+Z zk=PHqFXr-YQ`T>9lCp5xcoq(99ox(gyP*r8vc2pS90nIP%yJ_C2Gi8m*jiad^ThLS zhM6ZP6{i@c&F{Deuwpnj@@~40WA7+FPx{7hif~Q@&vcy~+^$BUZFGNB31?o+1i9B~BCEj>nQ?hPUG$w18q*HNWjY zaumh+Nlx9Fy?-BV%~vbc_JS)UBLb~V$7N&Kq%}7;Ha535x3***Q$fvDhufp*#-08Y z#noYeG+AeUYVhZPq*-Hrs~Db)-Rw4V75(STsk{cUzMJutihTAK*?lTQ7W9DF%n!L{)S;3Sh(1fm=XH zfbO1!pnw%4X=|U*-bDciOr#PmV>J;(MF^ylA`Tt>109Okz5?FlPV@1fopcV69$R{= zCbXZu05yc=VQwX(859*Rxc$(Htb~0NNHzuiO{`bq z4QFTKZHj_>@euBCNrx~iU6qHi+0)ZArDqB@IB;uO&QWkHtkCE=7FKCLnC5oMUZ?gx z-pXsiX^|qtg7sQRk@s(sVDs1P z!y8XO0TDo-hZb^ziX7w?5E|mFfK#&AJG+$u4>eeL1*OjL@(scJ9w%D~`Q; zFf1Nqt&`+MmJIupWw{SEvzVSmW3&lqjMmL?;+oIpgLM0~avchzwr646U1Q_4sgrxg zb+)%Uf=L!37{)Cp2lvgueD+S|J!Xp$%hw3g19_do^_v2jQ<6)VZfj45bdfG1!* z%adbqtS>dBWzx=5#R@ReQL zKRIt*cQ)5HFoV6QK6L3ZYsQV8**W!~6BvgSFGe25+uGJASYK03D_e9(bnCYn>&L$r zu$u|@hT=7r4Ni)Ec8W4nIf_txfWi=O!u)r^bL?bTGq4~rAo@gNbbAkvGGeSpP+3QC zG2Db4Ip@&aK{MJ~+FROb`zc9|5Ycf>M~!PbG%_O9!XImw{#}gbn6KW?KDnCgNoPO% z2D^Ce%#9mnbo6g#8<#G|<;eOEE!~*E9e;i3bX=F=SH(y(I(^FO>w0?TOyO2$Hfuk8 z#fsUpPd;(Z>=h@^nSJ7klD_@Hvd67TE;o}GGZ};(MC+^I(QrR}7g|&8MJg1nrDf*V z*(I!|C}wN^`q!^>HL=&w|5Bd%kX(0(dH3jeBER}cC$`+lvs{B)Ax{}ZrYpbk{$FGE z_ZiN$y?yor1_Sv}-O2&E{%#(C!Rm|{_dYwr82adv~;9i|fVy7Q-#aX4ohLMTHlm#j=6;1iM%vc%;y69MBpH znmKGy^caxu@m_2Tf6FLz_N{4cZES4qXzgfkYiw!6Z7G?xB=R1I4+7vuxii{cWcY30 zo9v5!Vb_^I{>87(ApZ<6?dB(Jf!{-uaO zH^~d0Kr9NdQrrvTZb-ivfe(0$>|)3vR#;y6Gy*BXr;Okp8M33-3dZYv@~SLSUXWIn zwWnK)i<_)t*PB0n{+9^0ej)$&zyHI0tQdJk$B?wxz?VkeM;tOY-5M` zsq5p1_B4O_{L|(wO1l1Dv!(mfm36sO+gKt#iA7^Q&|NG9oQGOjd@iIz?OgQDB$O!B zAH@{0D9{RFH4-aPpkH7qG&T3I@+Oo2h&a6wBojXLnHH)YbMcpjr z(o2Cl_YSSSy}hfwE6uOcM2T?`S7~&&Gm7i9ytv{S)vcO{Vp+b%{{)Y8?=~79#r50! z;1T{5-F;3|=H`x=#*BzPmq>4;d{)e*6G*X(l-$UxJE7daqKnZuoqvkC7=^nqi*@$H ziZ<&}K;DmnYcs^*8l4Dyo|lMM{sej4B#T+f{M=Et9Ox!k$bsrc6Gj9H5vZpzDDx~- zwPzkU(8sU$NgBzbrBU#re-v^VEO_5PI_apP7ymGx;w)j`mrtgQcUey1H|CxikdAg? z`K*;sRs6-Qd?~YR6!LIFOrv67I&{!^5V;Ppbt2cHn_?=+Y%qcu5Bhxh_T5U_@gzQU zb#--jb!RC5fo|W6&VPW_8(j_rJComSk0uquc!Edn3+5rsaPp9l|3&8ko!}6Li3%0t z6BVoDsfk%F5#CImaV;{tjfSe7f3 z@262nGaNTmQ4ssQFNGr{v+BJlwS6guFD=i0Z?=;^q7Cf=e><(*uXbhej%+hEDZvq@ z4t&<|Lmg6|8S^lJL|1roI&dn&_+OSQ1$*s{JS0U0$is5yG+T-79GS8B6yLymsZt)GCgMUBO3iS^}1L~B$(V(mBLhMtjNcVVqnxi95v$wHK;=YlEv1h#pEU*}n7)J&m1QmVgSCGwINj9Gp z+Feev>1aW>xmNV3)F2=FjV}ELGNNTL7MrjJfSAo(R%mV(5LK)F1O>4QN-R-X$8{ze z8{wAF2S}KhJF>8L&-MZp79%ZJfo%LjedwQ9C%YtBeZi8|c*v?Vt47pjrbW1{@EcwF zjU}rrJQWBlVhPKLhA0+wRuRt_(cv^K)6vn`*wNk5J+8~vv-pU5=IyZ)-!-!6-s|up zB^Dz(SeG#TM1ANV&^29>A8uMng_2x;?T{Z|hg^2zCrfV4IJL-f3sJH*=aFHJZ+S-t z$*iWDw#ry+_|TcZ%}+ufSzdcSEkiUGBQ0%l0J)L&9@s1-#fx}QmJs+!Qi&#LS~GS} z5)}P_bawi|UW>G`I*V~-h|Na!l>NjoYms)~Y-5(^xkX2?;c<@E7H1=i=Doji6t|*e za+U?|X5}kzIaDi6N`I~wpbt3KYqI-5^p(aIrwW<&Q#Mh--16r z8@|IrVA7FqlvU7wgYW?o&A7;-Ipp&vegF&s%^!XIfw)e^PMqk*5?ffr#NEg7>cV*U zXQ%6i17lY>cfQl{S-UKsm7ukJQzWPAesDgkb&oVc?tHuByLm`0kUQ^rrcmzu2pr16 zqtPaG{0DO9hh1-HEO6EvY9E5@jcRCpllkAU-rPEkLUy9lr5kz|q&xaQK(}@G&GnJ! z9;fsvpBDK?6BD+fD~*+C1nQ5(qNuEzN!#p!orYXDKITtS&V=)>eF*7XAV0k<*Va9L z>==^4lu6_Jy8GH&$BY|0&SN*rMwCaPjy?sJYnVMpo*Y*WxaI;~Af0APx*?5L(kWox0o4W#tWiQ*nMhW5 z+jTtSP`7IRVJU1HX;tafC!6n@C%pYO`zlLBP8?jIoi=!_wsY@|dwypUf=bQr{?2~D z=Ank?xxbsYn6>z?M)YMezir-Se#Lwmw4xp{@-S#6OuX(~7igtUv}P-uHm{;OpkKtmBcLJ0COu5L33A z-~Z8%a7U3}r~T`R8*h3PqwsMNH=%Ls2>Hhz8GfgYvf>iK(T>!G)c~KVuB)!At#OHo z7bJ$d%!Cqjg#|X?Crj- zl1?$q$d);jDc01+q?xHt+8SuO6RCe6TpuEVP#=egGU zAFF(LM^mFp2wZ~7dJhc-qk>YhVw}u+{L4=!e}w2vp(0*dq)|``^X16dwHR)H1a)?p z@ff_(X|n6eS6GohIiVG@xkF2N4oe#G`E|LHwra!zfQ*1+26!Ru$s+mPFtoF&5dLzk zN7bs^G7O#rkHRxrxOfIW{pP|CEz8B*I?^32RS|I&9K|(7q*X4q%%Z@n&#kx|h?RWv zde&~9cGuUQ^i^H%yO(Xc@rXrEiPwRB@NNHLuDIO%+z$`-RA1&Ve0njeE~N_0q|mD< z>Y9OFRza2E4TEewRd(kao(S7>Vs2r19=+!81Un7?ZfGBZp>*|Pyy2d*25KyktLVRer zTJ6<CSmxF@*t&c!mqoL3t|JSI zZ@1ebkn^$wmI5_*PP;Js1($K#o}l?dHRistKGR&?ENO2+lj3u%!c7aC=r~tlOIGcY z94xXiPdYZHIcN|4M0>g{{U?ZDI4tqITU_F?_K0@WSdlGn#$LK4cf3&B5{8^0bTGxy zz`wzWWhdA`Dp3S&H?vt{B-@s4Yi$Y4MZpy)&jm*~DYO!q%@SKnpeJE-;UAEn}Fcr za>e8W;Jx4T#`*EF=Fy~fp6?PsaoHeA0JcnN7h}tceA=WmcL{2I}Z>Hv*oA! z4Sq@n{2(I$VH=hzO9Ck!8yVVP;2sv4XF&4M;q=0lKp)Aqz?s$~OQ^C72y+pA5sns> zm6bKku?V5^2(JZImB@xc-V@W{TQayW;s{#J&SU4@fAu+EWgX_TY`yvEd+$YB*s{hte^GDNFDWiY^&aA_LE$Pc}Sc_&oUX z6x^{?Dvm4v-c)NUbc=oQx#tQk1@&!qxb|$=3IGobcd9Q(zKb~8coDP4(FxaJxFn-K z6c^(OUS-EoM0kNV++h52yfS`5OXY+X4gw&mm?G69bhbpC4g0w|vR` z+fL90;yFL?HOB)z9k~{C&Xzfnea$fDx&e*b-P7GOae~7nFV-oZNlG%?A4Uh4ard-% zezml4By6592F1O~7q(L=n_ba7jXQNbb(2z#WJy5h;BSY?paE&P(MWU)y{ku%#_W4B z>>X^YTy(wNQ>RSsndk_Q7sHK0cy^ZJ{zx$Yn2Sy@3NacIMge4*zrb1#GnD6d%$TU6 z%e-|$><>sHqTwR-><}S}j7^9brvjulQ!4pD_}fSl4a(E=Ish&GGr-5io9K8Ii=G3Iv{o>E5 z>Z*0cPtD%NODZl}f8Ew^&&s#phXQAs+q@IiB98;mRIwgKPePA5HLV)NV zVjq4q%{|di_P<0A;c;Fe?!1rik^RaCrRa9a|9;7Iyn9|MUYhz3-7^&F`6^4#L+xaU zPI->HABdgQ_J{d>kJ8b&c(!-n@AP*Od&9X{4*gU;O-Bm^MZDHcK4N(0e9^5X0;EENfOr6}_KcRoWSE7`^xIaQ0eA{yIibiMO*bTV@ z{BJ99ysuN)PEaX5?=H)YnLa>EcGf`yhfY7#XS+r#ddYUlSF!sgeR~m~`aTwOvdUT3 zMV^0n>I6UkpvfA9=bV24EI*0`yFVNkuUVkWbDpDaHwrr9DM;g^WGnSz1THueJgc1V zRA=f7=^)oy!EY6-a`1S`)j(Dp>1MIEgBc1gaE5M)sNLpD?b~eeu9w*I zXW`ua(j5HL*B)Z*dv|^Pj|p~X%$$vL-IeA^jNQgE+aA9BN%fxFZ`o-)xRX_X=@#>~ zNB(TSJ!^Uwj&N7qaAb*WLT(ja?JQo^{44mtGD&whiq<)nFqs4L&1s%2F-a zL|oVO3@*K@0Ow5ki{9Vic!j3RuSbqI zWI9e6*Vf)XzKssMy{Ty=q7vKy>I%Qy2K-T)%c*Z#4+H$Zr4FfWz{7muI(882*=$b7 zMOg2n=EJ|-^zRkt&%SlxZQCy1ar|TKyu%+q>$XoDvtMF!ww`#$n*3`m=3}ovZoYQY zgbp^lvEznMe)5_DL=o=kJL->L+4~q|vKn{s?;_q1rT!-14&ej3eFdU5?+}B3MwKY~ zX;DwBw3Q67I`W15)#mY{iRXK#MsC4|wHjv}@jW%l+8s3sWHgc!O{bJB5WC&4#Vr_x zE+7=@!U6RGdOl^wqmU50h2pN8XBQ~i*$53QIvPyrI&Tk$J76@UfHOjo z%gwi)bLVkOv*yjXG)^IidJ7qyqt2dn z!S~)_)QFDkm^JqmN@uQ(SEjftL0gH#HjwZ6Ko!|mF_2h#Rn-SlZc`8IOL69}Ja+Lj zTi*GS#dY+A1I(@SfBH-WxU3k%9E`jh*OAw{Vn+!>9U@t{Qdqb#xSe1sE_owHOP|o@ z1Oy{%H4m>W-fyVZ;)z=~nF0LshjwIplwA z_a6v|!}x_&5BI^+!H;=?`N)6Y%4`Tui;s?rA&*97<@O9WMIvt2%;79)<3v`J(*srt zdC`)vQD+r>iz`3EhU9KiIYii4)lius8H#f3;G8-=J%WP#wHvq%(=FqBjxodB;{KG+ z?RxMs7C0^|MIP!{FRHjpr4zWp9_aQe2z-u=yKHl&N+5ML)K!hmj3sV3ns|st$XAhB z>4XzxE}#`)^vewjLh?UMJE%>4o7^fDj&FWWsnt2geE{;;EK8I2G{X z`++@C1IKk1BNWdl+*p2?4k7>0LKxtoY&;iQ`fKw+_NRvONUPGM*!Sn^#TbzArO4ItT}>jh^#da za&M6A-}zyB!^9eGcVKxP{r{<@|EJG;fS`Ta391ICk_ceNqEXA$3lL065-2?f-qCvZ z`M^`}V=dsSfHHl{Sf;8v$gH{I6Wh0cV)H`}ZJxex!2#13EevDU_~h1)ef)t3KK`+- z_s;L{U%=>pq;-EZ*P#Y+Un$Ut;l2eGskXMNsv+5@WHQN& z$!DkGw-K1P03K?X4C1hN1@KR~Jd&?D@<8T@f^-qocN%MlOpB)w35Ky=l|Y3(vOyhK;c1Jb3|lY*e~{O7c*gS;ZZbS{-Y)66Ag*(*XCfbne^Uc%M1F-Wry9tKe5gr;hL@lWE&~}{wCV9PaZuAIOqeif!lZN? zkdnzx33!qa2Sb9Ol;J2@x>XAFPdTe zMLu)*Ny`sAY~{&ESX{9`ntLdB%k>Y1@83XuRXAk5gN7YgzN6bIcn)ZgiNP1;?9ZSd zB0O;`_tW-Au7~`y%39%%v~kF4l!Zv>)&W`@a_#nG@Yfw=>%enBwT7|HR%Kf%`Al(C z>o9%vphW zVT!c(SvxXh|4lkX`}nUd9ww5fVf)>FOF@xQeHHvJYV`O)d)^m}#`E60E7bEAyzH=e zX{6O44u9y6L-?vZ%p;x67x3L3%oMX#@Bfi*FLfng51U7p+5Rlwmk782@Z6yVP=`a0 zI|8zQ@+3+49C0{I*8|5M8EH7E`}qYU;0lK+l<~+2IKsB=r9DdcCGZnb)rQ>&z_%!g zEPOKRnb6hI(%e|@xE4IUDuh#VEeb&b2l+f=aB{-I_g%71Jq0kou)W+4hp`y>&a1Do z=GiNjA3po!lMl16F=@@X>mI!e{i`0)1>Hu7%~ZKZYz_<_!i0Rp4iasyC~8$ZXK3J* zrQrplmv^g(!%C>Hp$AbQk_~&XV?O+`5EAo2T-Pvp8n5|rT>1VG??L>EI^{q=oM>7A z9xo7E6R<=X9%2)*n8G18iYFBev+Y|X!b-zO+gP)_r~Nn3OtHAaG%pq*xzW)K-5^9@ zWPEo6!H&xsna4m#e`=ujazx&UX>ruIxgEJ<)De7#|~3s_e>s8o>8z0Vcg;YQ%LCVaEwHpq%W zcPaKDO7XseyzP6{wok*e@@}={c&J1F8hSal z0e);X);(-hB5G(F+LkJDO(DOWE^6X&W*tx!rKzE|2G3U0WkgjQTEL@bHPb3?rrXq+ zthKM#x(GJ1h{#YpKel0)9a4YmEw)w5v90E!!6zxOuV1z289S!_%pUEW!K>)MJzwSl zex{&VMAW%|#+?c7RFIXg&{YC2tAgW17rtp7CD(Car)Kd-ZDVhy*+1Ef`C0j&=V!*0 z_g;@w?Lp=$d@TNbz)Zw_Hr2t4Zv>MH8qOj~1=S2FTF^Gcy<;?wsYeMX(LgAuae_m_fpVBFZ!5M7)B0z;Qsz6;EB)Fn7B?{Yp8 zwcpw*UW*`5op7=0z3eN3m6%Wqfw;8O9gThxEX`-{XEUACqI> z)+(pvN>Z?wVr0AKa13D=pz08TBAx(e(SvFZtN5V-pCPLpPw^lTr`G-yhv|w6Trt*G zpnfG?X_HPtcpvGgQHi2SMwVrU6MNY4K~*73?6>9c642+&mZpR@U@;Q4 z7~yoLE7FxIo62~QN;LbBzN~aAbBEI^2envBhn^C|<|MrRmBvB|q`aha!-z3?*Rdk?htAb~}45 z|5^1DW|?_ng?XY`pb9<8+DtGypK8n z!s?d?AD~o=sxt6emMTM5n>~Ag9rAFg!p|zKY0>-5RY7fguVe6a4P`o{bNRbt(>dLZ z%7$Eu>zuCCun4Qh7ONq7h3IcW))3K8^_w>NR%%{1<*wAbsd?VVhzX?S)z?;4V%o$? zi3M0d%W?&I(Vc1TC>G`>G7~pmFAfbXAE=tO;Fyy`7`xD1Y5vRneXNAduRQlmkIPZZmn6eW2iyLwk3UA8pjXoeC$9oD#ljN`RrxJ6!X#M-jfDj9(+v$#IN89{!1K_i_W?>R|xXy+Hxdw zoW`5p!&B#3kcmX1G(px9iNEy{pX3&Jd7WkX0WQbJ+jiX{mm_?^xKgRC$dx3Sri*cH zU$6oBwrDAe#-fyn>m>m>hwi7AjO=_bOb9M8{7DZ4{<^(1W!MuZ6U0W;DS!mWN zlhI3u`faL0cZZHnKoSMFQ$l{{a`_!2Nss)W8Ux22t1YKIZbL4Ei(?$-0&*)$CLDQ1 zJS$GVJM_|K=a$O!8E?Ce%?Gs>$aVLQe3<2{yK<4%0?vcNreqXk-rMuwia{I*NkTXf z?g!hya&~=p_`R>YY}>kkSXLZ<3t|U#(t4zQfp?+{Q44Clz!zDQyvTg9Dz7BiIUxZm zNnau{Z#2rDlVd;|kHOmV37+;{iKz?QD3gh3qsW?T0JMUUCNGso6h7g*GBjQb4<$jK zb=W8VjC~>nTY*?oI)a2r*!4KYWKbuH)3&Cpvw||*39Y2e_e^Dm7wuDeXr&dAK`Jb; z&$4~U5l!QdX4Lc!ME`7_VSds)7XO{aD&JzS{F%M`XZFflgZF&2>Z6gxf6Sjm`-^(# zAKAU;V*2louf0ZVP~N|$Nh^?cQoh-^X`>}zLcN?MI#pS?Y&!(0QKqP4l)U1S7ZhLX zS(uf#ftydZWuX|!f&R)U)C3&Xom&`p$c(~a8ay9j>|Cj2de?^DWOIO{CWbjJWeT!Kg!8ej`tkLrp-Kf%nMrKV#te zJfL1HzKr`T6q`t-DzJJi4}&JXRHFm5%SS2aL;MoN@1Sj-w_9*!?f}}eUwS0J@9Pel zn!7^uuRB0$nz95jC!}>lC(>9S$K}dvsw%L>r$7?N>kv||#!(85#Z|y|V2wz$VIvS9 zzz@01`jFHk4p?=N86l`^51EnbnrII*GOfUG)~@yt``(LQxO7=zT$*iIbZm!Bp#33- zcDNX|nKbI7xGmy1a3swdN=@WNoq*=>&|d9)Ld09267glYE3K=ltEmPD9EaNFpa+d$BF!fQKDIQzF`WKxs-7Ht&HRJcP0u??c?AP|2X9 zbzcwCxje6L*<*I_!C=!)fjbPbpfzSMfqxyN28%RF*P-znGiOgdu9 zlv8f*={ek7bmsc$n_Aa&+(PFw;rG-+_BG0dlGPzRY&6mU@U6B9BBcCFRMUDSNLp1- zk7!#$pKv}T^W%Afq>xLrRPsHLIu5v|n|aboy@s6K8q6tmmveNsn@ei*CHzOr#`B{K z5uNdPk<%c#BYA$URIy@O{ z)%CCu+WZu15$BPDild;Ps35X(G*oNM->F z*3Ilxb7=1x6H694%D>+_69ZSu9f86>RyBxkFuJh8U^mf~Hcs&$VT%&c}M=6_#PNjl9^fc}U_hpGR|UX{8S-2u;A2n0G_~ zL5q{u$+-kD)szZ`9ReCkX}^sCGt<y}cR&vb4LxHfyIi572&()Ou@$QURj7LM1|EBKY_>I@it{7j z1#GN=T4;+(V9{p*`(H5fZ{cXU;uFj;geWnsbPm`~DsBZoFlQv!XiKf5o`o?!ZTvjz9`|7lHZK! z{pi9xsmCk9oB{EWz4r<*=4@NSOr^i;f1G~-`vXD_Y^Dr$R9ud@WY=!aOKJam@)*L01@S% zG`N>!X+pS{wiR zi?cS)Uu!`X*nyyoGDuff_FaMlL-4)F&q`!tu`F4jiHTozBbJ_Lald45Gl zazx@v;A$MCz_%&cqyY9Mxyd$FNcK#u!n)YRQx@cXp-dR9Qp;$G5~3OSj*>0-isY94 z_y%%#rc2+DP~t`+zRvz8zu62>KxN3pg-yW-@AGvmzNAzb(T3q(cl`+>rgJ=REs)&a+7S ze2HT^T!vtO62kKjnRhUx@N94PAD6!vMiJD8#-uE3oUBhy6Me@;@x5>!RCfom9JRMz zeT77FDPMKL$5)-9jaEIz`MRYAZ|&y%lB0YR;Loo8_P4JcJpP1LSBGYGPP*~z(+{5? z6*5}T;pcQZJes#kI-E4f3*v`?78jrff<=$P#j|-Feqzs`(d$I~4EiL>n8xA^q=_D2 zIF|q*F z5WpLRRUaV%L;>Ksy^1PFWK4<2qTib_!5Si_>I^K0Q=>C+B&yX-{%M^kT|Bk>Lz=8+ z=Mn#?2v`~Be&HQH&vY^w=OzQbUp3(y45h)>dSrudqlk_s2q|!qIiacpo1og_Jxnl< z04XAG|0N(bgKzwUly9~IbNs?Ic#j#hBkY@SdE{q;X8YK59?_xNi=;UhRPB#!Q0+rv zb7T`tOVq84hBg+2QSuH%OcfFBI#u=_RAZ)2rIL8l{mm7imoGpE26_JP6;pBA4brzm z(M{sRAsb%PXF-J}`}-vu^iS`fJ}|Abud@$T)m~f7iy3A#^9IvfcDguE%)*Zw9k=G!8kt{ym2slEN|}U5@Zi3I=RD+H_DHR z;1m&wtcie8dc9}3;qI6P3``%87?1@}J3Ylw-WV`>hui$|{&8XFqp|Pid3LZEYInps zJOuWAjO>NTlWDv)l?Nj(-m*Wk^A<=u9EH6Z4u&JajkqLN!a-$20eWI(pHbz^_~ z+Z)=$et9eObK)#M@DJ?@e!k9I=;LvGbdrw8dHhw$Tjle3By7jyR371r5RhkoPRJC` z^^?eeJV3<4kr)ml;b1Vb9`o23gNYe+6w^61L5)>G6WpLHgWiY!j+#MI)#@ zfdo<%)d+4N5Jra~o$OqZ{ti0CKmh6DXK?l`Xn=lF0qJC#_c;bB5k#iJL{b!KLJ2Sw z!p`Raa|pjed-0_|YA^CDlo$R`_SK<PyMDxF}SV_lO{Q*(?L$di+dicQn}xwTAN>FMs|Bva5H=h=jr^m(>E>?qW=-)rP}f5x|Nl0t7m%mBOA_K15-^N zV+5X@*SE;yl`Q7PTzTK$+HvBap?;F@Dd`dGYy^1IeU}~&FoS*37re<>NLci4hn*}q zLAXG{2^5&KM!BKq82O(7$M59{$I$HWn1k*QrUXixLl|o_zr&ES72x@|mULIuIBsUZfBG1=;QpZi9{gKV%(t?{8H2Rh$iA1NmOw^s4l6j~HrKW1= z@{A6ga5sMVc#$O9#UMm(pe}$WRrwqXw{@NjUh^ zN4eE4#o`^vSTmB${$WYBqF?BRU>cIYk8G32f-mc~Ns8SoX?lW`AN_rkkZU^#)j0<; zC4iWNiSmcONl4hUp7$d}c9DL&`!BLtX3gkKb)}pKtGsPLXgPQKK_1|}%p*6!FT6(| zO3zy`^xiwVJlWz)o{(^8McNZ8ucJ|9FwRjV07vJHg1aNjSP#))mkbc!eQu9--jboL zUlwxj7d}G&;6A1+mFhb0>tFW^;ouW@40cSNyXDqTUU-8Qzc3T9?Gh)}Z|czPLmY01 z;&4ME4%hgniht@9+=de`d}NUk7;rGzlrZuoOz^^F3*oZbjH2w!Hdw${&M+e>o951$ z+ATs$#EG+eaOz-dvaYMX%ZlCgW5JbC_nN*rXxum%(Yvl#-m*FPO|PAw3*xhu-*WQF zw=7?N)5#~_^t2h{`|QCdopkWLWy>-{KlIp%H-6+JUl9Ly_RQNSU_X0O>JI|2edEcBzkXJ4eW2}rlAo^q`LlWlgv_Yo zj__GeuuNLD%eDCmUH zG9F_mAqq(71o^{J&IX=$Py*$VcD;q!y6LQQzjXUxSL^KAtzCnDS#Rw7!YKG7G%g-3(BMbi!WYD9I)`grz3g-amH*G@^FWA|vJ&7rbGXl(BOj(twL z2nBxOKWDKZM71^7RG{e-=w7LwFns7CsX4Q89!bwJ@S_nO*({kWd*srGmYh90Q`HZ@ z=nmfRSrJ5GU)i?{g)M6Y&W-$_Nk}y#;~L_T@`LbC)%d4wmr%tCBP(c~>4-W<0Y^;& zR7Y%CL5`m@<_Gbf0IqEV(62RIaKZqL{Q8E*X7np7M?S}LIYo;Tc~ z?J@F$_!U#MJGEWk(0=nEzd_~)OnboEuAVoB zI{mqz>gU2RU+CKNNcQiZ=|El>(z*8K=vUxVn9Am|L)p8seqC8Dk~FI-m8dceq2nlk zBpeE9Pa^>%kD;BRj{#1(8<86t3`c`2W9a^<4pAEax+T=_9cCjpa;@Yyd{0?Vl44r6 z55QQ_U%!3{bxSw?0T}{y)!T#IfO!X{dgk`d?dqJ;mP9)Rlx-W2l+vkus;;h*2^rh% zk%Kv*Vj2=LOH0c2V%m_rjN8*OdA%r068hi~?YE2KgW7k0^oI7^J9y*^PkrNpv({fx zuASe(Pv+Gd-k$$0-u@H5VCUY~ANlf{Tk84eId^S85*>3l}QV@ha=>q z@p3K>?=z7S2LY(@f|XvZ?oA2#&lPkq*ViZi!>9nRf&)hd+M^;T`9lzbvQN| zcVumuSONM@mdeTnvQmrwgo+nbr|2I_DT)HYgNmaH74WRWbqOD$;=AjRZzxVJAv3g> zO1+?3m^nb42<0eLv5*N5^(ccdLe)k@hYB2LsOo`Z46svCS;5)lNfiy14XE-`6b-Wp zd_qKCT`48SrH$kqtq^@HQ0_n}lis$55LG+)pL?`d?)nC=-g43QHIoYYR_)}{nzOgZ zZ`!)$=G(q@SJe(agvCer@_SC$IF~CQu=i4*K87E==h>gWz30iN#&B*Ia6(-Jf>Sel zVSgdjz(O&O9^v7YHi*c?8#DDu!o1mQq=1VfPp3N7W)1WJqGK?CSer-?7!%Ej=B7qB zFm>-r4q%Fs2-yMJAY@K1um-QR>K{n>Rtorbv)A_*zDa15F!Bmk!;q&NK8x(^xgJ8W z5M8!s94;X3O3Oh-0LRNc!0nhp;3i;qb+os&G&d&P;MOl!a)8@)mdXxzev!LqLM|kP zUNVX$2)j?>;V(KK`gp?m6Tn>sdB#AC5FG1An8y=fU|x{>CbjXd9+yk$e1V=`f|M__RJ zw5hXuW`9@=W-Qr|1Cwd2XVhuPiA#e8UG*9MKQCqwocqO#6y%-*gT#v-)g5x;?;dAW z2f>SESD~(hvWx zo-oc`8wAa@vw!MRw2_BH2Ble2)OxB*QT>5Uijwqd65*4`rVn#u09&?=?e>JK@JZ3! znqjb@6wB8y%Jy|Q#(I#1V?0Dgy?z*LPqekRG&k+v`9L-5n&!}MMOKB1TnGstVpgW0 zm_ccWq~joj=nm;Au;ajZYT`gVaN_`h$l*-{FAhZH!a;_?o;Yxf{deKO%+Sw)1AeJh z(jpfUf>eW6wz?+Ue&4I%<5A0k?1@&^!B^{?(1M_~bSzj;bu8!)Tv+Hw4eBToj$@&4 zC>GvW7*WVI!8gE3<~Td2MGBA(pW$<|z><7}o{9z5q5>I%Szq9RI_FDpLpZ5?eLQ`!@)iPoftN5r3{X)lqHU0`bWx$9!&B!jcl>S4%p zp23d;6h%UEt*8;mOQ!|vX;zLz;0HJoAarr09rrR`t4NR6k^&NrdCz%nv^(bldG=}9 zG3J;`xa*iAao0O_%0S0JB7^w!hdYyX9XT=Cv1n&wv!X^$M4J3;ucU#q!oK^-u0kyh zH*YtiPE9&Kt*1IZ^#^X=22Vs#3n3Z_MNqm1jE%5LzJUsy_~31ylif%bYLjepq@HK_ zdUkY^4}ZFxkJUya*aP^SHUL$TYib7GZb_zlJN?l|RMtKaB|36YMZ3E$M=p{GeL1Zj z2kF`yQIA8?!gS>r$=hYqc@!CDhPG|yQB>oQ3~2j_2txjpGM?&GA)Xq?NTXI?gbD_N zK0?v;fNm%<92=*&s6*7r8&a)XS;~}yIWB7TMNq9H&*>zm%+Jf|I(7Q{JeBG&N(Jhl z&o*Dsr`&^4Do_H=D0pal2W?(*iQPBv`35=>%y9yNR$+gG2c0_Ckvdd=0cxI%<0vhG zQnLtsz!@~Ofv^#y@{QosTrZ-tC&@%zBBJ0E)L9$pGcTI=cmC9A-d*n_2l@AS7x~D^ z1UGpU_pp4?(K?+M=JH*V7tqqBAU+Mz$o-HG5Pl|Kgci3^RYHm345O&PhZvRe8X&ku z4j^VBcgaj3BSNB6_yt5A!)UZ%h!qs9T>`v0g=reQP!W&m>2E+K#0V-O=AKXdG84lf z0V7g~M$9o6rVac_Y3^csX?VwO9WA$9S4F+GnEm^L987) zYKP>c+Uqjc43Xz0TrzpK%HYe#7i=nlmaH%mc&`r`}$BXY?i~hWmX)?j-el0Wkz!0Lf#`9cZs^6 z3Q1C=Lp#|_4rmh}0?mKG1|=Vg|eucAiWdOg!0| zVX!B<9Ak5%3;62pY7`f})B3t*cF$~UNpv=Ly8R12@s+7ONp}35@2*Lb6N_IoYLFOz zQhtiyCDbAzTsET};T*nTf3ew(bQF}J2!~b{$Hu9VNEBtjqfrN1?blnvmL;@yn)5JT zOoc6G$8Xv!!f#KmSpw0vJ1sJMr+H8Ez_?=~830{Se5D6Y9fJv{wUURuxz}H?h8=%7oKr?=$XsX& zUTGHT0AA?MS&tv;WdE~2cp+Ff+GML8#4<66kVdIQQfs!@#=3#*9FH;v*}?1>{%;5P zR=qk(E`X0-W&+%17wh~yTC4xz}PgDuh&7oGk*CLu#8hiYs$ zzeGJUA}#@%_8nrXuB=nM)MT&63=jOvuj$2h#o;MwNX~3B)1S5^nVE+tNx9QjQY9B1 zL>_UKQZePCVQcCnt1dnc&3-Q`5%h<^mb1My>;R!S_2-%~iQ2J2Lo*>E&8- zqUH`qnHIw?*evkV%{%v35>6?IDq$S+!|PEhDx{Y@%PG_JIdBbxfYFe{DD=c+m+=|T zDPFkj^_Y2|hl__= zTxFT+fmp|M=^*OToq`gzT|Hg6E%J6Q_;V21oeOCNZgL>lyQ)o2952jL%|^z+zi9JE z%%p5&l7IR6erZNKY&zVzWE7N3U?>Vl(wea0m_V12v?*@RbWEczBl-*v1lmNUeHOVX zDAbe+XLPiu8+QJ(N;1|($&R++v|=c^FgA9zy9kQSpLew2$Ju<%{$NRXFj$0qbm)JI z2xt#KeHs%IYCF;=IXoC?lPKF2ttj1Ja!m4IS?R9og7comLl6_z8!W5TV_K@$V}=KP zxELICAw2%GX7r;2R_7G2j3a+?J4-c{?9_2urV=W-h$MK4XXzB=C*Fs6h%1qYPCEDF zI+dufO2>0WW+Tdf=zWz5yOTg)y5Wz?7|10I1eBqWswj%6ol@ueS#NYsgWxR0kY%Z2 z#_DUUtMJVU%}pg?(M7(#q<4eHh1F*_h?_y-q-eNU*2)dr&I5#?AbkIPd&NkZP>;MK?0HZ#CDGU?dc?dHtsee2;Mj9K?T~$ z{3{kAXg}4u(?}u0_L{_?ux+4)OCyu8^KR+{O`9u4TY9AgMS?1MC=lQ*NR`-J4>=y{ zuxNl-5xaqi3WWkgAc%EKBB&savKD4L?Pjk*V40Qr$gs?|SeB}4ZsKf8TT^#)_vA_C zWkm(y5NqU(QE`bRIHA6zkGNw(MTsb5QErD6>Rgc3C-QIu<$^D?lTW#2=V=R9wp8^_ zJoY*}-)K)uhS^$091@!O#Ya{)M}yk;)ZpMr+K(J?!s4w7$cqViVqKFCADn1iWYr}wyYc3YOJYy`ha8!Uf*v)#;Q z=R5wKe$|kJ`QEo(GytZ!+c9KsC;LK9m=d8TGm9d5*+SqJW#QUgBVEvOjQ+R5soNKv z_PphUlU8NIshM5t3r>xs;}Ld!nhOdp(cO|^mwLX;OhNCGv1*w&zJ=uw8Z_lfu z#xQ4R?t3{@1V2*^gIz~?YfVg1gQ^nT2)U&O0~;WC4Dn3G0?aZ1KE|P{$FfuzXA`R` z%1g!-7N8114X+97_u2h@R0SYVS|Q8nAoCyX&(Q^438)|4rETBw9e&K~Pw}pMqT1Od z*PeI7{olUv?9FBTy4W|gUr&CDuXueIKjW$EwD<2j_o_Eu`29aOeez!Iog0B5=%X{i z3q`O)>Jfc^RjQP8bgkno+=|*;74V1!7R#M>U?^t_+`D?^Z`dD6{ zJq-lb1mINkH-5c7epw2Nsi^^rs85zAN*l*Vsyb*y8rHu{6kvQV| z(y)&v%my^5hOVKeLZ#YjHyu1{(8(`vKj`Hvmz`METXWJ!)F;4-k7f%LPk zb*m9nkb9uuhH}ox+OMstC@(7=S4gT^R>eq$!cBAdKy!(kD=MaeOD0rI2n8h3pxAyx zNL~HN7Y7c~-fWoHaHr&xPVmV&rT=yQ4fj`kjE8T3au?@c(w@`q>mU657gc3u!=blM zo_ECZhq_Pg_ys@cul)2UiFMXr{l*JxPF($|?`^&0cNg(fCoGxOK4qnZ&zWNv>T;oj z{qBK|IwiumP94+S5+W2}0o2d8U_*jJuNA5vyw4H`Z^|SY@SM#+Y7RL=)4@u%>8Li~ zl1$>em(HO|KtN;v z8?gUocEgTZNE?IPri$#7n4aw7+F?4;i0r;R=Llfb2w*f%#+7P)of90!-9r|Yg`SK3 zyq=fmq>-SNRhE%BLeE?2WF~E#2iUYSbc7-;-R(lP-7t2-#GnotA)Vhmz{P{AOp7`F zLj=fWsp?vgRa0VeYi+9&j=5RRUM?{wD1WLOfSKX=XPfQkq_OKjV`%rOi!@58B9epA z16M^bu#w1(QTu2YvkmLT;;X6nZXQ!UJcV&1bu=efv=J+NG$Uvtx7gA~aV8V;UdSKn zBRA@l_;X#Q=r6~u~1P1H`AHIHjawFd7LR^wYPuSp7h#l zx^MGh{nTjWco~(}P>9Xu+pK|&Z1-)timj!nj+*^9BVSDeo8bg%@w z$mL1rhq0aED*&4W&JV|>j-J3nN|5tl9S^_?097x>HiU0eAxEWj=!tW*I8U-+Iy$Uj zM>I8Z*52CK)zme4QdMPHNj%CDJVD+%PF+MwP#cfVLyn4h@}OK&#O>a?&^NDrH>d7WkZ#U^>E>+B zFb)7s_jD$kIqPU|o|>FmhZ}(LrEo7a@h0i%bb!q6?=0fwCG|N<4zBpxV<*qOu%V`R zLf86ZsQTbp+hg9<2m8MCgVilXVeR2ipy;3@_WaaUicp+0DPNeuJKe}urDEkgs0KKQ z1%?N?;Oa;KE(er_BoGb<9InYDP8Awga@b#uoi*bUw#6?J~F+uL{;&8kMhibV; zH|cQ0w+l}TUliCUD=RuFW^|jnTohWo^`@;6CdGn-QuxnDdt|A_J&+OSkL`>+qw~Qm z{xNBtRuNUy!r?!;SYc{+jS^;)s+B-E2uFDOZxQ`B_`}Jx?UjPVycLKES9nCU^`T;u zY{>X~I2;%TaL75c=-y&!6_ZGHP1R4Lsv4o*k}xP7^GbZq1K&F4QL`sl(|LXmXEYVz zhbqDkvrM%d}klPVHU5RZzN!9gNI~tQPl#dFz#x5^!`Y%jn85qh4_j z{1@pJ+%>5=^h%(Mrw&U4m`mXX>y+ZLHx2lp9+`1J;#cq5vQ_M6MLK*k4D^K0zcs1l zV9y+jbR`_Jl3FCZD6d7gQ##r0zO1vBEMz3FGmJzQk(gqKs2fm@;s5SLAn&}CbeK8w zQow__2QgsQ%x>}pvnIsp%6kx}0Mxkk6M4JQ9eJA$p)nD8E9BGI zS{rN*1U(sn%j5?2K~J{f2LwWDSxy;s&hC@+Q)HZo zsE&=OjVK?e0r^0`@kXyBx={#`HE3rndr_|?RBuXNKEl)DIb4vFtxT6W6KX*-m5;7PJ-MB+*!^Fp8OxVM*pQI%uH-5e*4rh&Xov zs1=ykAb=TyvBYsNL7FiVSID?5h^nywV=F}*J=HkqN*f8Y$Qrjq#IiZ3(ZXg}*plxT zc9#CvTrZ&befaMUbbGx(#U~Xy1KVQh$p6@5O%n@r;SP*7d_4}^DeY{)`w=RGcoImNk{pDf{ziFotS#`Hu{p}`a}k2}xTfA2=f`P( z8~s>ZyBjiC&zT6j$R+r>Ci(TH%>j(i1ZU7MD$UJ195>HT#g$`5Qn~E_Z&eRwnz@dsx>r} z(9x-(07oZy@d@2@LUf;=bRuw^y6;fl9DUI17{#{+48{p4L=}_E_wmO!CFl+fap396j^O2MkvNA16imAl~N=!ky zsW{H$ad9SJfmU2L!BIqlyP)(_%Y-0@6EETWZmRFx0+UJ)%)~7ixdxfd`)1VKCCW>fEhs_l8i4wwPyRL5#($H&iI&( zEmI*7&gLC7I7=1+fzCoBg1X6-6ULY6O*A@rr*~DTa^73YjE2LBdfqfZ*OBR-t|p4H z5pjQMG!#61Eb-GH@aC_ty7$ZrK6qUF?mniycd1sUyfpf_vjsXx-bazS5B;|Wsdjolt2KPq}x@w240Q(RGk zPGnt`3j92r#e|Hb$N}*@V1F(i#qE!bjJ5$5iN>Ol7-f6XnPnr8w*tB&8dZh@h%kkw zApCRSAAU4F{%1|l$PSogO&7tq9@E9dSWKW;_=ZGejC=*Hc7B#uz@k|v786(w{RLf> z`DLk8+IzxixfY~w2iVT>xX^{zPBiNIGL8$`WvS9Rv#0ey0JM~FI+_k2}MXPuNUF7louojC{(7MT;96#+U?q3zJ52K zbk{YHwHEWOfn)gCTYMZ({6PEW@9)#T^JW*nDe(;Da-AIP<;%gh-GxOX^ZL{4q-B+#uU1_ECiWE zmP47Xi4Z}MAnG(H;zwX4a+=F3btQtqYBj!;VFK{yGIdD;9Jta=bl;)EWJ%+5Cokj9 z<1Zh4-_6GE+aS93c>j0WBYYm&sT8Y{MC=rG!#qbiZJTjgL7)^O#)3=<1aWO{I$^O< zN2FBlf#R|pky4W(+*++*KJ|T;XMG%GO4Pxt#yQyPJ zeH~}A;%c&~uD!n94N4=qBpWF08DsUf@MRw$R@?=pvcg=s->4#G{0V_bcz|Co3EfbO zn4ostv)!H=N3opkVc4zZ2v9V_6>(#LTW17v#$b-%c!OBARdE@_X+?J8uF4tCna?aM zsI$7R4n|N%OR}!HuDP)uYpYF`Hk3A$7wX}iAWBt4ccN`%y*s$GjDq*VWr_0f6+GPA z9K7uDJAX8G@rJqvgGjYMEja57)Bmq=8d84qt4|Xw5#K>Pi}((r=>+?n zr06b#yr|Ssgoh(2Z{f+lX4FewGi1<99Z0b*QI}|_cN3v;d6AU}d3~7=g1cQ53&~Dp zTVxc)|6W*7GcO;oxJibVWRr4=3ll5&^lC$#AP$uZd^o{jXvqDTCLuzJPVnm?Tywujrryy!D!1Lh_qhADWrx%URYX%NleqghlDJQ6{y*aFjHRUJ4t}3sj-oiY-6$!O4i#R z^K&RLDgyf;uq9Si;H+?x#)vS^40_tE>y?)!lx7sf$gqz_)~B&nG-~97I+@D=mXw$K z`2ry~ryC!Kr`%v{y;_T*-dway-au;SvIPps+SM2Kk6>* zl^yuMv#NRty^g#e<80r>UV5ObL5Z4~Ke*!2ZJn^nRMcNH1e_E0f|to7B~;m2!ihpI zvFXm~2>i}GA41hOKh>Xcs_@3RMJug9VxXEc)WA-rn+SdcYneL%D7SEUJ}m<%n=;$` zS4SF<;d?+0Woct3*=v#mW}5p)NzPc9V(xX=6~NLI_n{f>D;a#hzi*H{kp6jeugN8> zBnA$Y;DJ92y$<{wfWEChH+H3V9)E&Q%uf3+y!IMDncuA)M`vVRM#oawVGqBu341t> zBg+W^SzzA^!gs>n{~J7DPm}RQWrtD|=pldyWxgw+i8C%1&qfrKL{D+oslMqJuSG*=<%8Xk_Dwr-DuN`1UR$Y<*f zbo=i-23wzx;xp0k3Xf$-0a4G{^l3fa6s6&Hmh)qklqLJ74IoPv*A+;jtw3dV#J-AK z3Lgq{QKm{bS?PDrBi?0jgx?Z66uq5EhIg?+UhLGO^G#6cTC|YGd12fj4|To_I@RynTpD&>t`||E+GymAjH{E#OmK#VAp=y_$Xo9kot(1SJZ`<~ zY^&}ArDvtdRnSDS`(B3G+9hB8ai|(LoR!ClNAS-0iy;N7t#~bX~ zJ>uq%) z@SGgXs7GcIkHdWuk8CWABFqMU`3)umNe#UU256e5C;-5TYovboux}Em8x+f7;dq$V zgg$IyK{J11V@=-wIF z_epVQ_M{C*33PBZaRg9A(H1Wj#c?dW5eNb)r=e&i1rub!5d}pZ{2|{YaU@@Y&&AMa zmB@!yl=I#u3yzu^)35CO;0UKQL{4++!sr7>{HyLxj9HPSe$U>9QJ&d8f*y>LkNWvT zsdzt+Mfu_cQqvbn(K%ml93PDptR-}XV`+XQz$zRaj*nA3kYxplQA-V@3w^XepngBz zMd(^|FL{^Eg~@jj8M!!oWv+$Emr;|$dB!f&Uq<_q^qJ|%NP84yv`Bvonnt4dZ9Eo_ zB8Cceb7Nt*@DWRk4(4$8O|a-? z9~2kp(6BzGH-Zo2@fbo`W3e?c*jf3`$2rh89}L^pDp2|^E`~Ej0oi7VJ=X{~nRiT&kG)g#A0G^uM@)}#zh=ekx$oC-=*SpAr*Dtk zrUD@5{hH570w#Wf?)<>Uc4z+Y54tmGDxbKHI@49$LMb_}R+(u}_N7*3jf1Xt%xd zckQMpxAT*D@H{SKTR9tDAYxlNufRpe?8ryiIJP_mMa^+Hk7!C<*pqotg5|Lx_~X|u zp)$-X6*5%G=rw;h+x#Il4&yiiFGuvDu=V12G>(mHN(ADS?Wi*>Di^B44_jWsD|qei zhVN`yRJE}A)WbvP?0s)?fwnVJbixsPzJz%URwCBciM3U*&!%GIP$C(Hlu?gSuB`?s zLZam4+9k*eVCwR42zfjsOR!`rlOfk>4m#U&P^uc!&SMu!^Qc3H}qI zfKCKZ*Ow$pdi(8rsp0CMwV!EAfA>4bj+nM{Zz`~pr)*n8HAT+mU|;oOU#F%@qX2Qm z__ETH7}kIU2V;N57VHsPZh_u>4$q2wo+=_}5#;5AWy^L{G&QLx`(0Kt-~f6JZz>65 z19Kj=FO(N+Z)!``PwKPet5FB<&?cU)bHW#ilmd+c|d11+nP=(lc*HCK*yPc z0i9@q4x;fIbM_Kz48_$?G`3=UpD`j;i6Ubt84*xNj8XbeWG5me2FNNet}dyTG#B=u zIeR%dC;c6w+Z9eaj67~RGZjgXfI$VK{r-?nku4PQBd)js0~c{dJ&4n5yg{e#@dO)~ zAd*;Z-;s3d9E}Pw6krsfh%N_tJBAU=Dyk|GO&_wGlW5bf}9O? zc{EZO3XnXYBtoS0kV%UaAF_z0XfUUoy~NKHN+oMG!0Bf~Hzq*gv0xP9MNEiNk4|K& z$pvD4s`vWnDnL$?*fmDNudBZzZBIZZ$}BQQr=+%IL*1n6iB%Rxi?U!;N;KS(_4cqT zKAusLm@=8=QzA>tdWbgVb!{o%T-x`KZ@kGHv|qe8u%>@f&0y2gA%5M)i@p-_ME=-? zqnG2Ce_nR0_Kf!S-d);07K-ze!jZ*G)hDm|^4CBQ>&9*mEx}z1b^iX5q=#`R#H+^9 zHvoE+D^x;&o&;=9KmmlC2Am*Oq;gTxFIACJsLU4dPO?a^tp&tW)Ct&#Qmkk=v~lLq zsRV<=4C4BXj1y408x{5&amS^1;;E^eP+SxVu?F5CYIfB%BF&tt>rmw`PPtY6#Z=`Q zWn_CHhoRh6TZMh~RPAZ)wq}0C#4lX+`5mQgzkZN%slLIhw_NniGba`EuOo-*S4P>c z+jsBiKXl}?xACgwH{Zb*??Z*9lkYiU<6N%n`R9+c_cy5z{P~%u-+KQ?PrOF*8+Dm( z58sNu1YK+vyD?SK2yNN~ZCZ^Y&j2P^1>g-rwGxDEtO19z4koH7Y6mLS5kQoCu z;D-Q{&Vx`)9a12!sSd0z-bIB#c~)Tc5rtYk3fz!8Ei2@1d-syX zD_gs2tB?IJUm%?Q_|F$p=YpU*c+#HlxS)yLSHN<$PNzNW)jTN`4aAT?8VpA$cAqZ!)sV^d-z_+CDW=JMbkxXDF5g1H_;xY~RuJq4lnl9Oiml3o4t2-_b)m%WTSOZbdfNRMKXhdG)oXkWo=15qKbBgOHh=@gDGoqW#%cQ zy})3`6eo@{O@pvBXZR78xP9&x(o&}7r=xd&q@~@k&I7r(>Bp8lP#RooooRHOMvoUb zoz7m7I34eho$BJgUM0M+Iz^8y;i*5??JqjaIAv$l#f^r+AXVBFeY_JzndYRZgc!Zv zFf}qw6Wmv;%ggCq)6lum#8V4BmTnVWr~$i(iW_>1i-eLHwyb2AZgb3Xf;-d9lx&f0 zd@74>6QBK;={D6&-pGTK+N~^(JWv*HF>^W$d|f4YX@BiPm6C2PBxRCWcDqnaabhUb zG~F%~GyI6U)Pc7PMbc^>cnON^)jUuVTx;a@N`1w2eEdBReMPFL1E-kCaw_#A?z402 zD^fmaG+|BhiIYxdpu)#oXfG0y0Ku-?RR9URK2e!`AOlr@%=1mo4Z&sBOTsDOp=N+`#{194kv|Eu@B$s0NT ze(l8$vPxYWhHCkGrz70x|hZIh?54dKDM8uX_;TmJCUWYV! zxTE1(32j_9)R*JG?v>L@wHJ6ZzYT7mmpq^k@9E~vd$o(57y#b)U5Yqa!a*zhWU2^3 zNue5qqodE1NXevdl~(sMqfvdCpqqNLdqg``%xt!eMaZaZ_Jf@{rVuiUTAG`hK}YnT zSjOFrC)Z4Kvc&H8^4{vz|G4h(Rjm_yCmnOCNpa3S@BR71zdSA)F(o10f9LXn7*0DIOMKxU z7cU%1qV3hu*BRcqrNSpi#1k5P{AUYgL&jOlwELwaj<|%pZ-O_0BU)o8TaY@$T4P9U z;6YZ;1Hqtut)!%c0}XTGXw#iPD8Aj))J&@ug~vht8CIPM=;>SduQ#t&Tb{%-lH4dIbx?XBv4NZwMfQzn|$R}uE zm8$xe;@YpgnAO+8H7iQBhfB5@QZI4Ik>7rlZ__@e4QapAzIP4eA2`Yoe%htCoR7{m zMccNG%^f?EAC4nnH)!$3(aQmkuW2mQ7a|#a*bdyF1So=$ZuKUR%!6wbqL>Uc1d?#R zhDy#)a6Dqm#W+-w`i@IT3m+#d!!ANp@+5Et);C<*_u4&gYQN`v8(8L&uPT?T7rxKb zahKoB`*@N1%h69>+ZN<5rb(aSesVtLhpRi?792uqhgXucVJymWuW_mY@;~u~}IO$?C~y3LsmiR+7a~+hDZO@|EnAo#f z0w32vV&gQ<*m(M|WvN(yvauK2Cw}$VzS&)Z8Yk;+Y)@Ex{@AMNm8~lmU%Tt`*?_Xg zY%4*1C(d%gC#U`bhwA!^c*?RANWw=^#Z2619B}=m>{~#3HMRjfQ^VG!ViV9Mk?aUE zAqb02kubVLaXi}y#E3gk51WQeh)^)Ji3al^PP1Ny4|-UamzUR+PbwWR*egm690VP@ z>!&nKvEd?~=?r1tqCK?AbGu{jx=WaOW9Y|5$ui?lfu}e1^G5^win)98iM9YI zkY~a5-dKaA8wTpA<+7yJRxzFhBO7y0NHtqyBPu2&yC^qlojN;sRL?|?j;RkCTR zUP;3+mJlu#Jk5K>kdBq*m1R1k89_NN;YbHs&t%9)Da&>M*YVa(?! z>lsAp+julEE;tS^QgLvo5H_V<>TKiNer?3Mun$6NPfO1vh7>GX6TO3JC1pgtn4y_%gQS3fZl2&=Sysw}Q=AY^Qy5)Z}i<{tE)mZc_^ z6w@wbB#&(`QT@Z2xE2c<+w100T4Lyz1}hqWBK%6OId&D+9A;(6xq`(>FmH-twaRi> zPG>HOhp42ml$-hnBE|+1U@q}HI$e#}<_L)Cqn4!_%Ew0|C?v|zj;gl2c3fekELs)_ z2g0GC0A_))r?UEJmVIg03m=#^%WHd)$I0EL6&RZ{{$$F&tALj~BS+&?HSF_pe{~c> zMdg`S!?WSQjj=VE+2=71$5SE)V_a%7aL7<7o}!G5ciINvd;|KKG;zZCQuO+chJjR~ z9}|rkGqfMWp`j>KdYwS1cc!(vZ~NGb>JN1uYGwDx9k-JhL<~-J1aS@yc8U=)Ni_zB zBdFpm+eT61JGzcxERm);S8Uy50-{OM$A<8_P+)~Mhl=h3ULb^)=7ze-HPuz9Vp?n? zZi5i)jLI`Y$i~9j_p(CDWbWPNo|%V=gqh&?8aA}(UfO=0g;*1x@=ypgD{Bx0nyRse z5bgtt+^k9X`-ge#OFJ~%et_Ta|Afz|t!m=O&ksK;|9TAItnT|9{^uMW_hoE_N$rRQ zg5zQpS|2!YZ%U%cjJhNUF%;0p3cOkJs94CO;<94IXxdO_5V8YhIIi1}Gqz2AMG&ls zhEYX?Er2%>RjC{ok@BZ4l|*k@aFe1UKGYKEBspx-!jR@jd@3VQeNoi7lpl~TZ_=lL z!~FewFST%{_eyEweDA)+c<=p)vrt(9d9a{<;5h-<)J-9r5}2|{EF5kX(okaI1%Ws+ zfI@=n!-gU%BHNT6>yKtwgJb3=%fKfb`rMtkVh z5k{I_*>@dYcMjqXR93>ea1}yAIH<0MGm-j`&}((9Gge$w5I1F(xq8PPJU&q5zF8Mv zt2D37;nV0IdrUbG{576+q}ocNA;ejgq9UzAUT=6M0J>rFrXWm#Gw6pf8_&lF=!y~! z$>wPTIQJFtiV#(4#|0$67A4wB(Kl_|sTXqPw1(dCQ<~O~J@&cJobt&0;BP))Q&t>y zdP(^A<748T+_2`S$e_WO- zpMXpkX=WSi4rf}ogGV-a>|wcajqS0QXuv#50EcOG2!Jjo5>!EgP%!@J&^%fay`N?s zBnAGgHU~`Ao&?R5>--ca$_KR|cne|t2D9ok6A_9&f^6tm)W)=im>{O@RR!aY-E9>@ z6O=DBxZq%^v(Jib7BmWZOg2evIEWkqggqlmH6Vh-+~(9|V+7j>v?G=;APqxPh)ob2 zSrULIj>iz6NSZie7$1&;VgSh4On1dFvC7@H!eBzqC~u>#g9__uAk|kU_*{z`eNdL41eo`f;XR581GD~ z$dKS^x(uP}{^#rT6c5M5=`|uwuXG(Zf=*fbfgw1I>jY};kxJlqF_bLG45OB%>gwwV zZnAQ2O;yGCoW_69MQJW7GboBbX(Hw5euEXL@~+qEhjF;n3<= zn#T5UzUSp@!NKQhS4LL7di0soPvrcFz9n5_+kd-t`>%q>eDF=}51)GRv;3v*nFHM) zol88U$~6Igw5B9GPeQQSS{mZkOlf4U(ui@?Gj$Y*BWs4W8GRVa7S&AREKxrxS(9|{ zON6rtThVi0cDl*7$L9`f-d(s3k1}_rVuO@!v?ca z4TOJXaYbo4$RZ4HteY&j?Nnf=34WXXh~S`$(3p8zQ1+#*CxEjFEhPYR5QdYq3H`6f zEdJ|KNd1)DbJDul;tgyJScwCVVl!zNB)WtRpFh^3~S{HQ2*6M9)<@qsm z3hr?WSUFptDl9@Cl)fJ0Dz_dkU9c?M7@^P)TuGUDB7e5X#7#}6-+~YRGpTDcG28K!V2iu8Q{cxgfCjR|mY^safY~=7!L0az zt?mnQtx9@^Tnmy8Xo=^YI-V;XYq0=i;F*Gj!qVqj1U!!~Evzi6%!Fq#-kM_70vQPM)klC%V;KQ(83fSQ( zlu?2V0DtOnH>E-Vz?Kr`r7&QsQ>Dhgl;!eglEejAswE;wa3!RO7$^xotSzn3#%mSI z%(1QfZGPvuA;_k{g0Y8`FR3fXZoGuQrppNU9m#UR{SIC22+x<41LPH;`yF~OU0zAy zhEL-B_%}=`!@M}FOVz|9IL9O2CW1BU;hRF1l$R+;x<;HlUJ&jl=apLaeQoLYzt6V? z`*#iQeMh}yaMz%E?dbb>r?T(!c;`B?mM}?1!w0&G?zxO*dH6tuyvBi%mZ<$veMK&F z?^YZRm37da0JhWhSYI)nmSL@;kRSeMG1W{E zt1^z{08lY2mX#B@3`QD&D^LdED93F&y1KtAxoY8cZ(bc?yX_%0ky8n_$n#{u)%Z>+ z?tWmS6(Nn5F9yK_YoK~u*`V_;u0F)|qKJut+qN-LQrZMXC?Z$u0fYo)rBFar1^!Xr zBtppEP9NfaOG)|=_gk9MhdAE?n+r6F6Vr&kDQlgS zwk_iT>n2aE1Wd*^H-w0TN~W2;I~y94;p6~3!Mt1?RYbQ=W%emcKJ~rT=RE!SE489EWU)*XI(_tusLC(iF|JVfAu z-H$bG5qvUE@SbtjxAPvISzTTy!F#q`CFN99jwm!`Fd0L8udpava+YwG$~_!3wpN#A;*P+2A(@Kuy5KBTUPf4VuH-ut3=Zpo z-aR4>23q;MmYqQB4jhbzMYL|4Y+7l3gk^d&-Y(v(oXAtUIM7Z~ipSn0zWfr-+beXq z<6gshT`qqgt=X;#Y3;_TO+w~f%jKu;`vT<588MRcUnXVKVqasH&e6_BM1wi0(yfUc@+q$?w5jjryXGMi z1V?eie^*b!cgL~X)TAi%w`IgDdPY-I!tkU{<^w1_UIxVxYEHsz;eCU3iNZtbmml@+ zUwMS>`N`o2a}?>&o{o(VM4$dW=~xZ#zX0#AAc_+;VT6uVaPJHi0h+cn1f;cI|2N(TD;y%n;p#LW)5^G8`8AO(9pba ztZ>!f7E8q9=RqCrrSK-x#&9oZw8OGDEWJncT1(s~SB3CVoYg}pz~-?$$P`Kym^7)R zcv9`8+Q~K2EhnT&b|N+c4eYYrTq++R#g%!vQbj`Mz6KrnfG%@8Q|%@_GdM8K!>K=F z_F9ly;I)piv!I}reK(JNH=v4dS5gcyeVhEbP&Y<>S|`BBtxCW$T)F^~?$UK~y3M8Q zplbex<5PSVo2oZSazKMiX$&#DN)WDIea_+N2W#a$4oapg4aNU5>$gY>sWhmm8^ z$d*2UTbzLCS0!>Sph(cx8={!5b8s_&3QQ|0M}*3bQS?V3!0{{P>zF?x93MY6vc?1= zPLZcW;rM2r4tk`E+`e+GLI`2oU$&t_QuiNkzU`%^;;!9Wf5Qv46E0uzv5U3SE*e>V z4OK$A{N`IPyY$Q2KR(#ITivm7;|&*#E>$00ciGJwN6GU_`#^mx+Q(FQs>Wd>K{gTh z9aTUNos(!owdCe8H$>`%l93LkcBocg@lQPez9}mprAN?=UQ?=DLWInIG#kbOy_^lU>tobjx zZR1F2x^2^?U*;tr1frvhXea8n(Sz4rcFSkfn}Ex^fJ?cLzSLCISY&iXLG~#;u@FCO z7&abS%|~!ugl>~qIw%eVMXqn2mq`Sxk6ExH zVN~z6bJ?PZptuHo|5DhSH(@x&{pUf!=1-RQIX#feizCA{t?LC^%-AFc7EEqyjc=IFaZ%(y1hbgocZ-{{Si#ytrO*qNP zY?Qc>&u1pZ>cCMmhDM8nybElhOd-XT213;D$b?qz^*q(-`Z&A}c&}$esUoDq@iKTb zkeVvomIM+?L^TKigbr1#2U1XMd8$_KTdz@tGoNb&5>cC*pnc7JZ)Y+vT!4oEH=RN* zIkvjCi;^$@ag~)LZEkJv&rcc*1W?Z|ikwUJ&yIX=?5EDy5B9>aC)2!0-;dq2@ZOI# z0{dxdL~5aYKM_H2pEKP(#PA6zuO`yeM2#vV}l z$b%o>@fzn>Uh&`~@6GR1wgR<#wHb%apD{DV`8fELt{B@MIvp@+ARNPogPIFLr5Yje zh6z~>8f8OdeTZNl&@3+Jh9M(#nibD=mo;csT+>=3faAt~WMY+4@^o(krjtvKg-aYY zMgPF9x}j32aqSoH4xHIvBkG5yapR4ny|zN3(k2c`*}K@3bi{g;qV8rrVE|8bNgIRf z@M9JkENoi?Id5)zi`QvOueoUBodyOzdka-D-SbB9aluT648967xRRafSQ(fI`D{p@ zU1?h)#e55EwO0I!+~Jk3b?PHfB_LbAo2f6zi(#x+mSvUT{fw}MjAc6|-9s|w7&?8e z4%13D>{xq^d&xu{Azabx6;bNF7VQ}0cm3o6@mhZ~c|kmvzvndR2r*zUbF)`dZnnr> z7L~M#G$uQ8k}WbZanN86P(;!)7$Hqv=Sa#n@nFUdd2QxMD}cyzO95NTvb4pa*Z=R) z6!m5B+Qy|K@x*fnib+$5OXxvN4{4W8CQXq|QbUJPBcvxC8`6}jwi2$>hd`53IyQy; zc+sE8X)Vs0=4_&i+stLJt?~-pO3tubw{5KRQ5`$Y+%u7&2Orrova3@W_{cT}w$Z<1 zN4C6e@D<`Fq^zxECp-2t(ag~Gz|LgzM{hgNr$jSuncNCP*(Vct({zQNz>9UGcd%S{ z4O^Rvk%> z(Q=7-lG&c^mRF2CUvvhWEqIGgVOvei5O<*tqj_MZ{Vfk7-ViZnLfDCNR#rwiQD!rX zVum=CW*wbs69dsV4A5GOC~g){uxISSK12upx@+7ClEjf1w^#)Oi3c97N}V zXqb)T<3flv4pOb}KC1OCn|P=_rTu+MqCqJiU#foeb#42D5AtJQf1MwT1|HjAz3t9B zZ~MwuzdHE|e#Wjh_!&<;q22t(F74(g_(C3m&WLKeK_Up6k$r8BgA0MyoMgT1>vG*q zu4H9%7jWn*48;|U9*{&MJ_d_6US^Uru8on?V*ti_rD)NWO0>4rz;%x5(H$+lt-Uf% z*$xk?hnf{0HX&SZ`+G(>UFS}O&H=dKT}D>4@h7Z6RpKA#W`boudtE}Zox({2D0I3S zP9HP*+uS@Q5wjtxl_`5_5YxO~9dq4FRL;kffr6WxZ$tL*YAKzoO+1Ic7Yk0e}Vdb#=-5WMf+k z1>y#i1I;=sR}55yC+IzSl4ZPEurnvlPC~4z&$Ka;rn$G4vLTOx((6Zf^Zmr+3$MQPh7s+zJBZO2^Y@gCTfycZ*S3J=FZ}4( zMQzxTlu_@5P7=9Jr>0`%$gZNBc+zDe3W~-c$UUIYjXVi09ROmB=m_EcE5L(n4I|5` zxdqqvRqecYd?-A@=s2VcuZlTbY)}_n!S}RvpIl5kGep=!ylJ>MF?Oad!a^tA^WNar z=V(ucFxz-u$5-~wbqF$p_vj9n?&(J4Zt9qxgH4qRbumogDQqZYh~ex*NFb%E=^_ug zskW;;DVM1wLpC`i`jBOk&2eG$<|65vMWlDf9usE+$_YIxrHdD~k$PEw8neMQCOv#+jg)A*kG} z?BF;aoQVnv#fknmw0Dg&g7#DG?c-LNB_B@El`WNiIQr}J7VvBMWaHR?p~C~ML-S-I z2z9kv@6x^?w`gBo?D*6%ww)oDRo@vo`KaL!c$^OS)+R}MBfoOW6x!yLt|{o_EVEjT z&5BMwvUY{9%?f$td&4Ba^4+wQXx7=uusQxS`B~NLL}cXWKU&2ja?o>77$d?UG35DX zU7mkiO0u>X92r!#ay?Z+8Un6yJ81HYjs86 zItizi1ga$2RXt_7W>xi$8WtI0r)_svw#Ws-XRM+ZL3v(8OtrLG+t~nnTEejt-D6Ox z2f@`vkwP^T3J*~c0y`|dj*a=u67Za2eg>}>UqW!P4W!mbNUSbPwReC`C0Ki>P8sMJ zkgS@nWZd1G=?q|(NW`S}*mGZpbmGoB{|+IJM&08K2MinB0tdmpTeNrSAO-6Fjw6bhAEC10u$6tTyvh&Ye)OiSh>)2~gS^S9$ z51qQ;iP4T6yc=#TT(R|x*|QFwQ~1%Xt7gtTh(G%`WU-lcjjVZ)WSb=qqrxZ^`@{W~ zoo}7CyOe>sMhZyRp-3m(NDjxD^)d_re^yJkS-Qh4=RwtvlkCg-%|#9{1#hz5Vdyq* zbhjzv&^3e1y>@gIkrFNrvmiW#$aOoIyi|qnDUv6s0J+B$yI0KmiU2Ws0_kg_W_KZ{ zEOd+|uUNtmg_(q4E=!w)p#8ciqn1&S4#QtNal?iaPyF=+ z=^6B}dpc<|6e}?IZmJ^-2$ni?PEPe&^RnqP$1Gi+5q&K@?2v=!%$`0ilRhiXWp� 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 z zurWRaxyqiP0>V$jaXuhJvC`2c*7yiyhb#MYWrY|*xGKJn!;J>EpR`$$v{#V9l?02W ztFsf{QB8mhX;zq{K=}1IoFrwY1I-%kI~_Kg8Bk)?#(wT=kn3-cyICFKo<_rdp=mpJ ziu->-AI4!%KL7laVhb|*?hy;_6JNf(TYU5|P1?3}$)i|qigcG}j^%v|pGeB23hLub z&P8FgnhI3EG%{Hf;QVrXq0YdUuvpMw7CkW)Rfe-#(|!MC!*G+rVOAg`n5Myt)o~cw zaFm*TU548FvGTyjxYqDJ`&@;{@`cjBBk>i&%FGDyhcn@zxbj* zve0h$z|$zIk5qiwP+2bS&hL`_78>Hjk0_$vMLk~qTnN_}?jMvi%n0BsR7o;lg02GW zld6<1bH)MN2mO>x=)!UKrc5xa5E_aLZ05$9t%|B@Xb`1oCCHY7Im}R+4h8uo1$a~c zJGeC0_krL`COESS9U1B6s0}Hs%xRxqkWo-%OD#%?Hv?#$JB;*fEQJuUKCIJ+)gktgkfe`7@f2z6_(!=fMC}bZ%fU!^~iSIvHWD zViiNr@m+zpB~?@P`Xe7_CODJR)6=u?Pamzt49M1yqs7oejm$!5I}&0njoXbGR9HVG zy$JfbZZTK7^XyGsKy_Su0Ai%$yeJRN=3h+wizW}9VEj27Kn4m;W>kY&=�yORL$U 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≶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 z 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

wL~ADya{NV&li63Fd?vy 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/assets/fonts/Inter-Medium.ttf b/mobile/apps/locker/assets/fonts/Inter-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7e573f64989c71ad7f7acb13c73706cf8be6742b GIT binary patch literal 694512 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
  • Cs8)@X-fbsUx{sF7jq%)HIeMX zq^<1C+<1QfdMJ4ZyLfi}GL*Q7ouabj9v{o^dEo%%H=9!I*8;8Z>lNwpn&8J+Rx9cr zo0Qd9z0fQNza+=`#Dbq_-9URrB;_*seSP!Lgj_aGd^ta!N4s~Ic(|9|X#d7KiRAqiw*3*@N?3PeJbhwLEo5Cs(l6%fP( zkO%S*kzGX|n+PI^in0ijZKi8}-*fM+s$137Gu;98{qchjOfp^7_w4uFbI$_Z3%|y3 zEY{AYzR*l7LaPT0!?8G+0mHC|n~L$U1+_K(QB@IbD18;|ig0Ec#@LrCTX`;#z)C)l`9 zhY!EU1wtpfcEoJ(5iJPy%N2b3WK$*upNH(eOzOVvbKfh=6Uj?JIitEGqYqIwJF<)y zjC|kQYC9luJt7a*^V?-T>*jLbo8w)iRhlVa8R;(41C#2oe$sc5S>6S@VfVWW^unAe z@|DG1ppc;#pxaTcBk2UHq{wer7=+urF+k!2cbOdCP`pZ!p4S7rhYmJ23|(*eazooS zHvDwzq2S-}ouyiDa#9fs8imL$L0$QrW8!ZHZM6(Z{ zgCu2@0W}muYsH1JvUr(=V$_AADiSujY_;duGPK=wpUJeZ0gfv5&3#84IsBK^$Jn?%9hnZdy+Pbc6f|SH z**Nqc(HC^E5W0>O;78c{r9qWsl#ALEMRquP6b6tPE=z~$t`aeo5NDwMM_uP!MHV$8 z0M&SNlHZ#=o__`zWj=`qU*UR>n%HE1PcDcVXYTisUCvTrbU_J=`4+s+*>5509x{e$ zUE{~LHr9`+phgD;d7%Jk(@hCtD5)7u`do!=wX8gRk@+y#^yJ9W_(RsU^`KX->sZ!Y zI(dVBsN5{}dLJ!0qXVZJpqHWJ+C)!9v?%T$@@r@GL0ZYSgbskC=} zU;5bkZsqr6PpaSA*+1FPE3jKhG*#^6^O%MfM zS-2_2QEI*FwQvmyKiTB*(*4#>_X_#UWo$Imc+O0T3HrCcFHN7jdI`an71TWH)! z=Lk=R!^kn9$0^wzPoqAAX^jo_b+t8PDk#4(8rE7_E9JWfGx~5d898yq&F%2?0<+<~ z9rxD{*tc2aRd)IZP3F(~&DY@-y!Bo3QEy3I9!l*tro3AdJHz}#joWd%=h?eZyGB0h zfBxee!-IeRX!~WBPudQ8Owg{}iRRUIRcbeiHe$g*Y#Z_@sQ=r>yf`MUAz~1Le~MK8 zwV()y5el)ji1;#I3)Pq1J9wI2e;KfitN#UmHvxb5%fYjKWZdEG znJ0brJn8I8-{t%8b6erN+%LGvJ0_Aj(9!fl{w0jRNSn}~hboFFi$)ltwjS%iD||gB zB5s)obW4jwchK8ZgW-MEdCN8zam!pZwzV;>t*Z^H(J?mNaO7ugIO4h(MxT=biPK)NF^4PUvjwZtqT~W?t4JYAB@-InnY3>DG@<$Y|7d>V*#nzh3wP9oqLtpd}J4 zJ^Jn_E%ydE2`Rxcx(4}w(bJ-E4EjfS$xTGpiz|}#Txqt9D33vZccPG>*@3` zIu%prsb`0D8ybx$HkAIN+EAE6>?P?Il&m9#5Ilr+Y$!LrlyAk5JC>OpHf29~8)_G` zd91lW)*KkSLc)^1Y9vok*cI)HeVa0S_ckp0SY-6h85!A?pMPtJUu6oQQ$cr-^9 z!eGIC0J-uG*eqpV-Z@~qbLIJcGibP!)38~alT0F_?bN=y5Qs@aj&#rLNkpZN=~kOs z*FCcX6b1z)Zup z`B|3w@g>{Rp2gi(53h}w$f9bck zF8b72kYDtniA)3@l;epm6$ldv#Bs|R*Edqx;f95nP{VkyWAxK@=Ve;) z6LFQ=+-GGx1o4Z=#P5(*HlO_NcO7~gsnE%{|0=`tKEUuZVrTp4!D} zTdAFxjNv{zR6vQQFb^G|ay^KP)~Z3SSq%&6@~)90Uj{{qI;o3ljr9cG@&ybo&ho(! zEOj9ksnjYHEt7Ez9tqdjSL~$)teNh>`LO9e+>A>~Zg}AbW0zDEPcC2cC2M=xv-Pu2 zoD&QW8o_0s324}8@DKFQERJ9RAIMgyZ4l5lk|^NzTS5!L4jU(M%>vM&PzyCDZp4i; z`cq|mkhT}!R7y{BLaPHZkvw#m`J&47k5V=In*z?%?;^)?I7^A=MTNR?1bB>lbGq6F z9M+}?s*%1m8dkBHk{jt=yi6D4WEJrX%`$%S>z+j_5WAi>U-|R&GoC;HMXUqc8QTIl z^0f)cap*dtBeJy-5C+yDoh}e);3CIc#Nv^B%2^N$V|La8NnC8p(91%^NWdsLVqc6T zODxNMi{Urp+px|UP0p-nfMft)wlRp#yU6Pehc^>9w6(e&Yi;p+MAvyV(H&$hFDog^ z&x^;R5%kszXk*wI5oFbhsSStxx0DhqNT!Ok$TQ~8&26L-LLr{M_r9OOb=h9S*0Cjj zvf$o-?Q>5(^_;PK=w|aa^IIfes*iNI$a*a&W?7C>k8qTFNd@c=xbGxJR`ie@72u0( zCkiQSmZ}n*3g=Xo(b~H9Q`m`@Wxnt_v3fnv0>(u<`d+ADaPj6~q(8wwQ{eHHYj0{D zr3i0ztVgO+qzF4E+DZ`yoIgQ=M}d^>m6RilD}PuH$`P)#z(mfkK{;kzxH+KqNBmdF zV;4_w$1aDoV8z6lhlsRb2S_C?7;ruadj5mMS*9IDdBF~x6&9Q!^nr1KX@4TY6b}%T zS*HSD;?Qv@Wv2o=P&#?0#CJo(Fo$@noeR9?9t@^NTVac&V=PdS)~g2yScm!cXOidE zBeprqHTCxa1@r+=>aT-AHd` z+^^??p8gR33_0BLS>n!t=k_jl5FEPBo!jf+=bPILeTcnd<9EicF{MDy60=6S33Q_- z`Q%`h{)~m0!>|KO<=x>RHMmbr<#kZ(qUZ`E*^F=%H)nC$tBK3vE@phD=dmOd z74ytuJqwk0$V5)WZ}GJ;$WVsG#LM<;S-ioYbxb~09yc}NvKJ$(8_?5=D!lD2iH4$C zTPFP+Z7P;=yTcpvg!EZP*2HRuQ_*?u@kQ>L)q-c^Y@XHo@T_BOCZ5qJZl1MgB4bRL zcAJiZWu2KG7gWd4m^6-c;m$NChK>3{I=?D4t))4QS7~|vql}_59#(iYKb=?31nbP) zQio0Hv^Pc1<~E~PJ9Sr4K1zEni_rj28*~Xf#!zmij$u!KL|S-fSIs7sT%^Vu#SU_* zT{1P^!6W3P#>2~P0o+q$Zv1!c+<1))xoDXg(zMVSX&4A0un`beAEj6#Gv1A@=UIf* zd>@k-^QU3?t#ii6#Q-vS{?`h|buxKg!PrioSN&E`Y_CDMtT67!7MI*6q`&7acN zSTFIe7V>G76Vt3!cmT@iT^olqJBAp9It9}%BwOSVQQv0I=o`Gj>pZ5GD--#Y!Mof? zdk}OGz9dbZ{m*2)snJ6R^g46XAu8;TFB#GvmSm9cxT#P@G;rpr+7w8$4ju|P#c7I1 zs*qs+(LTRq9LlKx(=4Q^n-nYcTnzr5$2RD3t)8(siI7!J*H_5mFtu@PEFP7L6-Zgi<9?WsCG_{BdoK{W>R^d~Zdvt))eXl^7!HYi)`~6D@ZEp5mk{wA=T7CV$;cIx+NHEzE-r z3TUuF(tj6|cfXU2k8KA1RP~!&kdby$TzpBF`8A7ls<7FVYogJ+@LW8@cWsd?9EED0 zfpw~}8FL5>y(^OGiPkVoL4cb7oqd@@j9HRYw(u~uz)HqHE zX`kj3dBvyllrYT}OSIMC9U{=(L8I;t^0WXqiJuk<6e&f&2`;6=3q5$Wui);&!XlQR z-?PyFYv?R78xLU7d__|-fF!=p@0)V?68^e8W(eJrSW%`}TX#3fLig0}sop88wmcM% zA`iaqt-*KlndnBQ9z_%Z8N4^$(@j5>xWvUfVO2Sz2)PmG6CpR}`pC^e10%@|eVQ&e zQ%7TZzVI0QxFp18=4js_*sP5#HM0=v{kTGt^>>UYGfgBh=~H=ac^S>>`pOHlH!}4& zq{_>;)8)nQWHh40sK+F_m%vP3aaUEPoxhVzhILHpuz4n}gW!>fst7GYR=wE4(w>#^ z70Fhs;~P9_Tpttdtbx2X#i(Xl4LDP2h2j+}j=n+=H5Bba8ru#e1$PK~IreQs8|liRc~@!t-_VR=1H&vCA_;oA`6QM0;Zgcz_&fODGW6Rua92~U^(A|76%Yx84eI%^ znZ|lr-&0(KpuSd7R#aVF9ghYIgN6Kiqw;&(frH=eBCYr3^lL=|AiagI78w2k;5jCK zuY{*wJ3M(Pjd4U-PuK|W0C2HDlmmA%4L`JitEmy-)zsG@(>o2wJPBlL1|Sm*V^aVq zdtmP7&2#6iU!OYj(9N3b^Gx*Rif2rxiPo?%4 z_ww(o($)(5)<9ny){XFH2YtHr)7MeA^iy=4-fHbf=mHCaG&VSoQa(H~zsFRRmJ}7{ z$D?8Jl%&JrqT$;|kbqm@-;a!-{V2V2pg#N?l1J2W16t}L1DJHC8`uMN{nUk75M=Y*^!$T60NwQ$B>b>kMrH=v`ydLAUkX;-#vUIzWZ3@C>!WQxEk5{fLVqkszU)C ze*+%daKm~k%9I`XtHz~@p&L8s5vD^-osWUz||NsI*8mf7?+f>)fxvt(6CKP z!U%z+bR;5O)wOdWKAX{-&On!of#hU2pV9E&r4l1>c_JfC<=4APw@4%3eDbQyTuy5@ zrw`*g4z$Amz+=cf!NXexWx-xFId5#2IBz5;>u{!?8el(){?qQ@8LRDp&gu@j$Aud3Ovfd zcw+$)8rggY8%mYb*V4=xC>mMAk|K2RsNgKMlo?qTXMYYakT> zNZp+x5mGW_wL{4i6KmPEc36{MLs*m_s~w(cVQyAr4c74gVBy}_`CR^Rze~NH8|ao1 z!+Xej$trR8;usR6Jfhc;eS*lafHi&~qD44MGdSX^2@fNGeE0t}I8>wBDa z_8#8eUesT|X8OYqMzno5uV1FKXy83GX)zw$=;7Uyf7-}CS1A98Ye{Rg=YWu5Df zKx)nt`2fT(Q#1?n`ghC4%9SwHE+@}f-1QM(p9HCg*00Ls0!lbO85a;60qzS8G>itV z^0e8z&^Z&#l$W~&Xtaj}EC*gnbw>*)^^NZ`RpiT)wQU z&lv7+f)8_}kPnSM?4IH%M&8&c7ovel07Bp_PMKS5jrD6=G!i9&{6IcOJ0yW<^+Z#H z51UV>Zt#Ro2_WA-VaOE-u9fsiXtVAqj>iUUOm`sE1IXjmkQ{+%rZw8)_q>50;hq=gMy**J6sL>|44(_2c^aDNFulGGdJtHE4Uq2U zy`O}{Co<}0O$^^Gv)DFAFiJzgO+-kV5ceZ7v7e$*)<~Jb3ELKO!OvFw4n(3KgUVPzaIod1gx^E3X?jTtD2EbT0$ovPR6Jtqv0E@aDrQbADK^1OAky~fh;02 z8|h+Q(EArQy+8D!cVj~)debLvde?f%5(=wkN|3wJ`;Z_v68${X#^q=>#LoDbm)58S zsHt|*+0rS0Iy*t3z%R@vm!#9#2>|&~T8Z|!&k;unVX8_$OxOfSdOqZ+*gGE*oZN>E zOY2+-$J~7y;jn@4bf1|lKfrZxazHAxf6t*A-4DocrlS0SfQSu6kz+@9-L@4f#~R={ zW~d@ipeSn6`G7AjJ|!De!uQu`z&guXTaAG*MxrbLckPez+Mk;o=fa=g_Z!dSzTZG3 z{=#GoVF`+o@LqU4<`2h}>1WswafB^rOR^A;(D7Qr?0N#j5?mj;h?W^{VK9y7u}5#0 zxAz=y_r1M4wuXLb8giNFww{cz5l(aX=#$Z7Yu5KaB=Rt)+p5r>e_0ls#?9Ez?2qdbOXr-t-1y4C&(e-M!ZqX8*A5=Cy&-* z@z@SrZ-(R4n6V&|&xenlj7v6OBwAVo8ZGTD?QN~T!;v1u zzGcNDt)EU@QsH@_=uHQd&;)!Vtbi{zeoYj;&?8={KuKXsH$X#AAbbgb`tzwh0UWGA z+a^uk&y1&R9zHL{pPWvt9g|(ElO6Wsnw*s4{~Bd$VqecVd%-11zA;V(A>UA^8^b*; z^xa429I|{?{w+Uc9sM20Lf?4}N#KjJcO_y;fL#`F(X^_ML2!sgLV>T1heX`~u@~fQ zDxrsBUg9SS10px7Xx8)4>4-3h9`v8JYxBs&w=1=IRFj9R6JIH;MxvH7l`zv|Bh}}r zEVp};DO6O#tsXQURx9)zzKWS3=pyKrNty|gNhm)liS!N2dZ2#9E0LBwlKyesC%E}M z!jB<}6t`7~sDFBBz8elBMs=*#+M@p`yZNF_=y zhtH?(3Ve&(q}azP1Vs+jp_STp|&Hs7B*u3AC5)$^J5=Mp)eIIs%s>uK>|H_*H9 zPaF8B*cdTKk$R042 zx0*?bq3db|Yo^Dt)pa|@tf1nAn%&44XDdal#_)UOA4&U-I85>o_q@SOQaj4TL6y{o zj6*B1DdA{0aKfZpy}^WC7c&RX6LIPByy+V{#^H2q&l=1GWOZ_!^0=HfnCW3`rrZzk zpEIZ;#BY+y%Vg|YLCbti31Pbt)5G?pL1`xt%cl8Ipt%2hs7!EXm<;6t&=ccUKuimD zyTN3ydS^p@KJ~8jpDVPDBIe!fuXiqMQJrkCDvJG|=9NGPuBcXc+ztNjy^5l8I)`R) zyH`pbeC5yxTwBoo%ZER>j{)h`UNFQI)wce1hCmEB#{%UVKFDj%I`2kEgX(A z>eMU;K}a|den9BneHtlnMovXMAhg;cR=vuBVQ8-mwYtH$MhUr!^Gf6xyPgX>lrq~v za!HJ6aKl4n@z!g3pkkhwD*|Npv1zE=6?=6<$coa2H5l6GP*=Z4(u+@bCphJ-1`AGV z(eDP+Tae`ekqD;-VU2ej5C@xG!B{tlHW6h|)}TA-@cUA(swdfICua!;LHxf?Lc`K#>*hks|sw z3+8nC`j0~JfE#qjWCIBY+`)f1u~|2;Z>7yb5HfH$VurkaO8eim0x2EgSt^|p!NhS0 zLb~dskcv!7j`Aq4n8*sLF7F#u6SaYAqUb%i9d3SAC%ZG2M`e0v3bsInmwgS9wrBQFjl|GVP)!09+h=-?Fnm8iLSkY3(S*cEf#vuI&(^_ zf+-hJX*MAnnE3UXeu4s1B@ei|Vd}Srjr{+3r4^^Ne&W_wTR;3}up4qyq&1-h2&P*g z$#5HI2g0{(t;XjTG$opg0+=YlkngEAHnp_%HHvI6G>4kZ&`9B}+s$`wWWDSM7hQcL zi)j0gGG8+9Iq&bcpa0eSF#L_q?^q|a6?s8LxEI{AvjoF5th}IhYDy7Ak1iZLto4II z-kKU|7Y+~jW7Icqmnr1LM3#f z63P)c(S3FidPemmF^xG$vq82|nk25+gPXi$1YD)XrKJQGHQGv}CX}qq7b5d%%H(u` zqK7FanwfvN_K~FPWs5lfA|Hc)nb*OUgIs5o|u>nuF3#}Y-_ z^yCzqo-_`SLyt5!3wA+WJqwZ|4T+^e#cE`ZtsA6>NaZD(rrwjht<+X0QK%y&NF1-p zDTg3gET+*tG(rcnd#JZd5PuE^PEwzt*4CvsGHSTouPms{#D1n+z@)=`g`IEahC210 za-=5M%^q7W`;)yk?4Tpa7+CKHY;>UvBDk6cjd2QP$hrX;61@WJafX=<-7BDzbUxBx zko$%_7NoyyMCO~Z2KS@}S{H@j#*6Mj1zG|14iepi8dOjE0kp z5IcM5X=9mj?a;}g6dD-bXsjJNW$-H;=koAkuJ1+KX*-c1%k!hDAyp&xB9u{R$Y#^^ zhK2BB!i-uxVR5y^6GMxqj1Tg0h;Bl0cw?xAw&3N^4gmDvO&ns@XT#fXzDOX0ToT~@ zve%QSz1|*aN8S+ZRB=&0QmiQ1VF9=K&`;UZ3!j(N6bIs!(oXfaRD|0?2(nt%N;h|~ zgReUC@C)aqUN(+3znnU57|Zn;voCKv;q}hy#i?C`-!?8Z-%4GJZx%U(QbuOEq?-}| z3rIkEgqiH6)cpc(F|{@P_aw@QHb@Z;Mh?JqiuHq38y=+mNRBLA1pNfux=YIpOH^zl z+{l|B-GpRF-0tFOZYzSj5EBjJ&K}hvNLs>;H*Nit0`GhuB}K#1qjwaVAz?yum@Hf| z65RzM(ZNDk2jix}Bcw&3B~&|bf`l{5_hwRi4k`a;+Oz@~4H^iX78rREvg*c7LzfFv zPHUu2uzKgheq)XG9G6MS2^u6IfR1X^YD-;gL^LpLm#>hzgXN*#gBA#_)T60A+?Ks7 zaVXY~5e7^lxkQ9Iw1uFUr%->YG30wtZ?-fLRwNU+J=F5!q2h3H8Zq7G%eX1^729NS zl$8`&F z)g>haZAn>4S!qIrIpzp}0<$3uodWk9GIDOv^Ge9daTzqbZP`Fg0Jui|?AE8zEVJ~5 ztp%^J*Wq7I`_JR@y9>0L$!TF=NLCb?odL_QCVipmnvJ4S78u7DDhMH`8fkc3&?63> zR!s*N6LY=dn{xafS6ou)XFu5T8|Y^~*`<7Ho`yOyR6_s=U58Y{hMWLBK*PbZ;sm8J zv$7yJ+u=U4fyPDV51><>k3Givf(5_Wm>Os9Hnvay#dKrW&@G_d@DI7n6Yry%BJ-PU z;$fEY>Qu$TEC?LaWz4Ct8k`#L*Izob&Xgq>x+oJigLkJQZx^JNh6 zvCu(#wcr!`rvx9{-zV&BWEgg~5OjnuVXcC;jzEy|AL)oi`-0HT3@=#EEiq^3Nx#Iss*j>$C z3HIN`^0^FE(mqPS2jPB{CUpG>96uoeL46w$wn6vM!7(zW2L z`%blbNao9XQjf1|7IXjuLVh9Jm8G9P5b3;PlCld^XHHyjYl5C@%vOsGah_ z3o&#~ayMa9B@cO60L34h zRc315L>M0tQno1%7<=vgNu`LgIgvcQE9I#|Td_-mBDW?L5bpY5Yw`v$)MrGghdXg6 z9bR=?%?e(na1QXA-M0KsPIgOx$#ziW!VS%+!cYFZ8-y-V*6SF8J%-2&gTF1;E!Acv zXYzIP#i(X1jquqvRhSoY>4%q?jX+zDw;K}kQN;sS+4qsl6UTlu{HxSM;nQKOE64*Y zgRM65nXcu*Z8a7Isl<%?AU<8-YXY%(U(cO?9>u)?TjJ5ZV##Gpm)-`Z<8l{8^*P83DLbuJv;h2|a*jA6Cr+<_xzSDrg`5ciLm*loii z6k((A1|^wCAOYoMde#q?lm02|QgJ>05iVZlxOK$|?z!>rA2SVq|-$hPBKY%#yp_;TqlCuCWHN>r}<&HD44U_V) zp(9_zmw#1NRZUe*78he9a*;VMwM&3ib{adRW9>k0b9MKAr>#4b^h3znChq(7?~-H| zWi0u`+8(Y!JnolIOm@P4ySPr_{zuiR4@c4|)z6&i>^|I;=iX)yS14X9$D@v@p(me# z*Y#46QF@>VGOAvn+mgDsVR814)#2<MmWF=PfB>$?`>eh;~1iH;jxV_#h4&WJr0B(%* z@A*Xo)J?+lj|zzSM$ch}C+g05WL0hnnCfU4+FDQ)vq#(7+M4+_EXLe7TEj*gU>GOs zEzL~cSIW|$6i~SsQoM7wJ257c{ zrQWcyo2~831?zkAi|pWeqM`EujZ@38Qi z!4(@IxaQvj-#w88`DSyF_A0JpkqaUh0=IVR1yhDxeQLv@Z{>tlbb`?6ylg`@xrBx4Gj&=4b7z_k38>S6nP{U zC|h+)HFMu6J8^rIC!haIaKQ1IWsQTlQ$7$Jws1a-)i&>JR5Dsxct#8HMTA*>nNpo> zw9{D_d__Nlk6ae;Zfrw)Nf|MZ=dz5#JUg`|TkOq~?DI@^6u3*daIkpE`=vl0`E3f^ zP2f&f#$1a&jwtejV3vmPvZ47|ArRVU`L4pgM>r2BXx+&!+yqItoxyE)Bd|{F!LaPy zut(x`qZ@WtZBf z(mllcd=Z7tan*#nq3^^er*l>ts%%+2tNbx=pLw5sX!d8juF&R+T_^Xrd~;$psK9%a5iaq5nvx+a&$9xQ9WXxq|n z&8=s7>_Ijzv)9S@I>Hn8%!KEFHYb_%!4wx-xapuzse&0^{Ra}{aB7Gr)pdYwK^R{ML%kdFT@3X7~ImlfAQG;gf z9p_8?d%CDX>%fgQ=Fo-6qX~rqYvGJ#K(ad7t~?vSO=197zJ3M{H*?rWCp1iGDru#S zG#alR)w;PW(Xyq;;44a*T6QGaZC9_+tvcV6aDo>8-9&AgHa~fIjKZkst+^3NP`n8O zD#86R?Vi*MV?NL~XwM0SOgpMPOg#jq{$R*Me>*y774Us;Ljus{F%sb5?jD&6d}JuDOX`M&c{z88X6AjpWH*^<8AuH zv!oB>_L4W(D|1e6*;41rLx<_Fe3@OaVcwdfW^_)wc-x6H*PSqRTrw+Ji7b2RCG+d8 zCtcgqGk;Rsx*H~S&vwoPbiYpLbIQtq0+A2qk9%W-b0iPvcqGpj&D3@BiX$t$f^R7a z5|ZDF4kVK^B9;d5+>VUmPr~mJ`)oWJ5_>i9kS&%Fb@tl@r=GfC{;8*Cg{Tnm4Tm4T zf&N3eh|qWR3nAY1Yg0+vdB2c677xaEWEpT67#Ns3Fm=j7O5lOd4jt{R;D|5vZEbGq z(wmz?3AlCH5@2Y{FYdZv>6Q(1rX0o|zUzXys}GykH!$~V(d0NQiUjWnee2=bvyVAA z@8;OEn65rhtsA{o24z(j!7QbfXaUWh{->JU?;=97c@VD$=#s{-B7~| zL_!Gyw*dqI{l$O5yt#)YXSOt*zwI5acb(DfEGTS2(CR;&FA{+uoG z%&)=@oW$+GbawH18@M4j_%ya^>7td#E?crHbi<_Hw;JgaKms2 ztDC)g)vQ^omLD=})rvW@j-{3&w!GaaZD~8*^Yeb#*ja+%9bC;|I5znj%>(o*NV{5d z5UNX|RU6G8paETLc8EXn?6X(1SteTCTKe>V9=}ovMYQwmtA51pX^;h`iiLQCp$Tjsx~IlX>S2E zJrpEoXr!P6s&2ABvwQ0MvsUI3PoLYYSQ~uW-4j5_?+bD2(p;~#Y2&m7$s<}>5Q7CY zGTHw5_-dm9@YiQ`6G_h-JI|)xtL~I2SgQP%xliQjH_}&=KT7jzA z7tDRbTFTtVw&{=jBy`l0O^c)d{qPxwWJTD}N9K3lc!RCF>9$Z)$HcbF&fawN(x|;3 zOp^4SuKgfcK9{5W2y7!_g(Z>0MC5QdwiZ>^ z84siVnO}{efIfx+qkbom70HT*hG|o9Wa}C~&Y^f-R_aP(8}%=5&zA0~L3X3LH#KDL zVK=g)K7Y<}vzIUIYd`q2XB{`Naz#(u!P!y&Y4bja^oH(rCZ-q!Zd=l4qO_~Bj@z4za?Y~*YE{$EdD+M-fU02T%4D_8HR->gQMYM3Hzd6mgD)$_SKj4s`Lq$Ip>CcY81Ng&jbhgLUuS ztDTvQIrI_sv^CQOxTo-;Njyh&8>oKpr65AB9b+d>7~9*?D@_LO8?qHECDkhz(xt3p zMQ+qm&KKl&BkzIRO3HhfNK%9>hhTd4LtufLSO{;xFArk;`0*3RPpqJH2)c34DIEgN zckU?>>}H;wo_l6Q=rbZ3Pn_kk=ky$;sWy(t_o9$VfLaNR5?jjF{m9C_K+WM_hu@5} zVcrG#5&WK+cUdr$D@_-e2>0754=5@b4O(ngMx@P~wBesMwRaDV#mEY7<9qCRa~$d5 z@ZSLcR!Q5j+R?jOTWHvWyu7U7o*`0#8FOKQ$&_$I1L)--JxAB4E0Xy@b8N#{;z=kA zp4ctcB3rXVb$>CnU&{*Bz&@ud8`MV=ocgUjRoQ)TJ)EI z_=}`l1I|Dy-Q>IiRSR3}M@BTmg&iG4v+@ek#z;02=kDYeeI!eI9qDxW%Zge?)YJXX zAX^oARnn@}>Y^TN1f^Qgw1`s*e+h`cNJ`bi;{m0DLc-Si*zIQLi4m+;0k*5whE|@- z7S6(sM|sz=$g9~=XWtiiN3!e)(`t1pljjE+Q!m$QDd&~0-3Z@}*p7G(+f)pPLqz*ywtQd@ z|9fDU`9NIW2=)%GPdhTX1edk>kvtq2rE^?;9pTeGaa><#Uu$z+ zM}3Feb`^|@TW)gbYtz__Y*K>!tX$x1t0Ket3A=~0s;0#vsD&y4RnTo2Cyqc6t#`;w zvo*+f+^{f;JhC8qQKK{pRq=@g+8oy=YRXHC(Z&s(RxmtQD`6$EczLH)OVfc`CHXHa zwhL+klgtmz6aV~Yb_2@~J~h;BTr%`2i5ueZ+RSip-y3Pu}@?1(Gdrf6DOqA(wi<+U}2 zL=%{v0J`Alf#$|BtPwp(aJAC-y8iOJY}yT9H-G-Fxtd+{#$%7aWqv-Q-C;iWzys!Q zewdnMJpac#?)W`c;p-x!t{&{W~|1(9vG ztFhCd+^q!Eb?r^?a65Qdq}6FNl2hwyOHe(V7ey;y%yvX+40TI`?*y*GiKITq4H$CE zi&`4;gpTB3mz2aAA{kbVMDcJQ^|24;{j)d)}X8K1#O(w3IS;Deq|a3!H4uL8o*P-I`N$7%{kf++anNLwe!k(;YK_$T zSQ8;=RT;0p8-N~&+fEtj;m zgd#%|EShSbC?>`1u4oLSrG+gQK!-J@+1#mLzyAUDsCkolLM%p>_YaRh@kg?}H=D0L z@QnGJxkEoQ!bAH4#Sh(n$CH3Z*xzShf2r?nt%gp>0wV0-a+Z??8qIWa;EPE3_;ktI zs@j@r1V%L*$6C5O%tT26DP0tV*8r>VY2if&2DHJg zvp>*Z60J}CVBWo1_EWWJEqV+$Y1@;rYEYTrwwj=zGnkv|a+HiicN7eBxu{OIsY7Ja z1;~03MZ_H4rmDt<`q~V?zN@6X{z%`TbWZWZ-1w|g6+*crWG@|AYozH3 zZmzlK&p244p%2Hpu8wf2YpeqCJEL92x{K=t*?8*R(7Rxv-TB&b=R82^kcqBc&+B2N!mxzYwN;Bb5e@@HVuy z5f$1x+B(LzH`Y^s1BLe^E3(Ra-U%eLfnpXt81E_UCj$;mc>7Jrf!S@#K`qsExh)iS z5K2czPgQHDwm5qLo60EQ8=I)9i67K^^H3scXm6P1p+Paza!wwXiz}b%RlR$fL){##>?l>C-=B{tBLq9a1)L&kDc(*x!^1E!x zL;VNiNbt(eZ!JD1T4MgsA?DAYw|tPn!kWPU4(*F*vHL!I&HiKbS0kyqlKXEs{X1R4 zM+&cnk5mr4kpGRYZmgs*FN$tyaQaY7gTukLAi8Y?;7r5Kg?-tGNzQ@Lu~d+?1{be^ z#jTCSB_$=54UrIav9ZoF1Js6{GARu>t>PSjIvJc+f4Q4Y`O4!zx^Ne(H6LV0nm@by zN9J$YVIP^}P(I$rcKqeRxj+26xgnz6V!mA9oqCemHz^iB7zT~lW&)j+1-R2#CH*YmJ7qb2W7W&c8 z;RO8f^bhu!e|U%;@$|3Q!S@D&Z(a8F+qSR!+}yIsl}GM)^y1si_rJzwv+`TMwd>kd zpPpYkdkzace;12febumbGx3Mx4}Z5y>(MS26zC;qH-NOtZFI7Ck%BwD{`AQCEl!>C zi4!$#V$Z~$?nyS4cm+@{RHD?ioGCT*9qw4?L@@nN(%G@-wgo^BXusS^Wo>krBmtVt z9Z{Z>N~udO`q9i&e|ZvDV7b2wDq$J)(wP^dhJ;>jbB80Y`mQG0}~OU>gl$L z&#UTkBEHpjEoTm--nBa!?(m5v5nhEvx`9iGV&yJmLlm@F=t(Vs?*kb&FTtv*Z; zNvJSLB5$<_tjzxHt)91A@b-ZJ+NhIF=ZNcdw4?O$kVB0(ks?UBu<2hwEXC*pO`CGi zK>wtPHa~M$rrvNJjj(HON2z&#TYJuI9eVJQqGPtRzrGsU^aaLFIz1=e?r&O_f99DN z-EzzHIdcYLwtW(M_5}1S9I{y31EJ?u%AVt5Qw8J;hSpezMLF+;thUgyT;Jq6Hp#mj zeLWW9we0rF2+r{>;*(V|t6xFxyg1T6ljg*Rz<;`qs>FxX9I0bH+J(Z#25Jv9AYoI7bPITLIoHc7e?5oS5hf_7=H_*n=m4-YW zi?C5p-*Z0~BbOIQ;F9G~e64@-#Ho{}ehdSkWOe3Dvs9xza^*xeBkYRXUy=H?l(nh3 z$yzuZS__hjBUpB_ro8fhf_|+BLccwKRp@63pAMs0$fujBT_dEgp9=0>&Ir!4VP1qA zN8wBeX9j;MJdhW;G**%3&X_jP=kf3IsBk|g7bAgl3Xju-u`e~X zk;_*uUA!VEat8)(xuw#4hE1w^Zq5qSM2lKz(46c7`aP3PovVFA$n}&oxsGBMYJe)h z(&gC`rOD-8>`P=TWQ1o$vTph`vbM96)8#@1Kbw^GTuGm~d`E$<1Pg(Y3>BqAp z@fzErK^Xttk|b;y}UImx0sMzBW6TnoxnFuno#b(D3+ ztFbuJ2oWF(LoIN&(1fzlAKOZIh|!5nB@>#9__bVPzc~40%t;|DK!;EY3k)>sT4Pg- z-Zy!selVMIFP+ds=*ec;YN1>d@vX;)os?!D`Ib7;=&+WDVeb?vTi-D19NKKM0Obo<4R z?l`h?a@pL^t=oRv*AW|e9W}nih>dh;UlxvYabrUS>8gPQE^JG9TkPtj`*#T9EU>Z= zaUY2xI*6knS79xbkyD4Q4bez2Zta@WpFr>>xY16Z$5SGJ(xG*|vO1a_{CoC=8_a`P6FYRL`N-4jf4%Ow>DRBm z=kaw{Tr%zvcIliSo%7|dCxZ*v@oeh@UraqvVgB5F*nIEumR57jxCd@N@2W!+Z|Q8} zl5M{}XaA3Z0-s45x*hSXu!fltz>ZlHlEHZ~>M*Q@F|&+D9VTRQ%I8DfsV|sMi8-hH zVxisGhd+-qtN0$wlHXMk(=nwDXI45lHlhQ%^R>9gr2rX-LNiSeqvS1f>!@`GYI=Mh zrHM=>NVP%c4CWIFcxYgP1y90r#FOyNcc0rFaQBtC4rQQ&m?wWb>O{kcn4gSTxzpLL z5!IW>Ot5DlxzeuAWVoOj|>$;>IN41D10$0JBP@*$vs@)No6sdPqg*4%wEKLA+tS%#Mre4ZK2 zDgDxtUD?!n*Y(UH zsIrDBdY>a%D$Nzd>(fBoz1QAPKpWgC2>9aE#gVdi<39MQB-uGz^GSDhoCV7uGQ>O1EWZuT{bM&W}ECNmIK1>-?&0l!fu2s ziPPF5k>SHLV>Dx{&funC2v;D$XV4+Xfy!!-6NC8pw)78CLnrAnU$$!T4QD;KL!i9(+|v#*&E-G69vv^pHkd=9 z+oJ(|NkUtnj20K>f%53tKL5^qh+>@v!s};HMRVwjxCYSJ8bi!7fa#}kO|^JHM=K@& z&v|YT!B;|)Axo&lIm(Cvbe)Y`jN-;VgAFvZ^V#Q87X<27zPFOKm|qyy%%?744;2@i zLxBax9cZx~Fo%%-S2g(lRp!az$GhFOeWu6@Dsc=vidkenPzizeMdcIPq|p6JYA+3H zw5*gA5l1fY6nOCZ@T=kJkOfTo68=On@fqw_B2`Jx@5syF)m++Q&2^@G19Ay~d8yXh zqxFSCtp5q~s@)%%S3QAEu<$Q$vjU6&c;E%|$N&A`Y{3h`Is2dgmH7cH_>~1iG(U0* zCVU;SYfJ#u0@m3D$$Dt6Ofx(^0Qr4DP~DSgE^U`Rmy7gpaT;U*aVCqD?|F9E3m@y> zadhMqBo;kZKXi9GpuutGtMyM1&5$QVG{Zeb0$FnCqC@c%Ds!+WMx46PhlV3Q!Ou+v zrKP16_?IXvXFR9DPu1dn)J~~%@ridefY^IA zz_!eO2%FE_W;z4I{-Bs_+nmZT)m~YbJ0Vr>zsrZKh4xX`Qd3b-oY2 zqdp~ctfQUx*B8j5Yk?oKzV-ONsQ%Wkf4)Du!8d*C>n(jG`&*$c5%?m>6HW-kaH9{J znLZTQWnn?6qnaNswR}p8kBJVC6FsjjSR;V7Q9IVQdsf?PnpWcgOnrm79K!Bpeu6_C zr@q)ina~&8k0GvJ*nYs9Pdk0D{mCEhu^L@l`t&QQPhS(5&LTvND5t+Egm%onbcufM_S-*q&K-B0 zGkwXD=`)rr@h493hqs-5&TY4ybM|fjwQTCNWy_{bJ(A~fa=WvH%VxE9yr5Gp_{qBn z!%y}T&=Ef8+%B)IEG@09Myk~qd$;2kgFcM6x`g|pc7&@Vcm~A&*KPq$`*!Nb(${I% zHY5wHtB`k(VnwvmL;Z)mq^)uD2bV@P=p8tVR(PI|5r|Vh#@c@MHs0n}ZG{ZoR7@7>BR5&uJji9auC-+hq*-*06@#g4ndWg4846!he`ZmAfPaW3@;$vIEJ1!6;5R!FVXfM{fCT zuGu}xX;MEP(|gKFsfS6|1h0RQPc4J}h@$uuDnY29te-?YKX>!yx%1YqPn~(_=FNxB zJMqNhMB7+@!UP}QaOmOd*B^f92J0vLz33DxWhG8Fu12FCW1rqtU53;Z>jct)jI~hU z48*s{j3WsN*X)#K=uaeMw_!TK$d7U&74&wuw6qErGDfX}d}zX~8xYw|Jgrx(A7N5+ z_=U%nzc#PZPeJ?FmCKhbTba6O`Ql|ORxVk3tUrAs=Y03O6?Dfj#%!6jYUQkhk6Sf+ z_NrBLW-VU{Lt@cLx8-M(ke?RJhSDNIB%;z5&jTlZ+BEuOoFpZp?@giwgdyw6J1F^) zMd_B?s&vsc63vDmMTVMIELyg5<&tH`>Sv=p?&1~4E?v6nH+MZWwRjPeCFd6KMq>{ z-1*>ZqjfXe%Ddtt6BP$3yMul@P9l#!i-&<8yddREWvhMnR}vA>{0ZJ`V`(G$>=XZa zpZ(1IZ{}B86~0th=t__GBSWa&PLC(Pre2nQ0iCbb3+b+RgScfOBpjXqKXM6Q!1Kgb z?-L)xuTyQHFN%MXQB8e<{DZ$V4+gAIr}dMcu~KhHe8*{53T#{LGnN-^5`%Scj5*Hp z?`$RSH^aj`UB-Dh*E; z%nl&R!vmp8JZd5Lp)eP=n%pYCM<5y0kq3|bOQHw+UsIw7w%y|U|2W)NTNywZTg7KM zV0Y#4g0O0xot<5sT@%JD4Cbxn{j}PB=2qIt`-=bXM`#`IuL;4|Lf?AWw^zKiznup= zDE!4!<^Cpid+g_%x#yVSMBXy$V}cr`rCsC4wly`>RM|cvPjL022Kk6g+1u0{2tRt% z4$ad~$L6;5*p-WxQt$U=OIEI2ws^TeDMIhQ_#&&8*$jtZi<>oT`Ep?w96NZlpn>PS zllyVVi7kzA9HL@i-tpHDIWv-_iF0DJxTZ_M^Ix|UEqc-DTcFa2pg4Ru(eFD0FGqQX zGcrzXFGmblcsgAAB6WeM<+nepf~LF2H{Y|P`G`GNBNlk3J6hgG*A#F+v2`nrS$(4C z**wTmEv%`jsjsOo<56|dz%_GJox-~rBkP0biWu)`0d^L0e3Sp9cv1*HGoErvd~SF` zUaR4GoSlq!qB|zP0->@~I;3?Vy~DR7s}EnZe3r8|4^mvp`6`;Xj=)!sIA_FtXscJI zN2Nl(TfD{H3iv(~^ytw(@0RaQixe=po`$SG#K`(-P7d@q89xr)TLno(_twlLX?5>b zVKMw9BVqPG>Fbk>Mx776-ifr)@~*61+vKKCBYp0@Efc94D>v(Qw2W5yN6$m%)$L+IqaQ`K-R^= zLBqfhYb|PMboQj%>S&bN@tctz;M_)OLTz&?Io51Fr9*M_YAA>+iM8|c4>%>DAFlOaz~ z3!esDc)kvgTX_jJHZaOANd(XOmRXdAYlcqJFGqL;hCJ)s@FQ-CVcK`PO2%%TB}&ovNjBSQB9aVn>42@X$0!h=??_zcxn zc!~#MU7z9@T2zEAiprvj^0GvU1v*{zc|ecax0WN~pkJt5U%JtW-0M1e*tpRQ!UXlb zu_y`?E?pIIUCSX3OME!EiK0YFu?1|@2Uxu^F(R03j=U20fcUgsHz(-+&w!15p^<@` zChxrcfit8`(A@ND)DNT9S)p7!97Dz@W>rZ!K=mz`%i0>GBWl8SSCkc17gt;K%S)#p z%M^qj5$-F~?{+;Hqz~a!WSr*ARcn}rZee40Y?7#0B0z|^QScK1RnnE20g(W;70ILz+=O^fqphupIQ9uZMj2eDo|pZMD53 z&*j5ui;@kMr3+DtT$U&pQ#eL~Ksljq5HMrSb}TZ3V=T8h>xIiyE$nt%oo$8WA8_)8vRWnBdJ@%XK=V{wUd*@9PWTtfiqt@wmiClCHQNR4XL618DO@- zm+8ixl#*BFq#L^*BP5V&jEF-C_$DMfl7<3>TTr*to)CH3nb8UEW@A5iy=(FK((+@+ zEZv-r*}ekv>bJ~yBFHF=#?JpV>4B#0hpy@Kude6$aAJmUsIrPn;M-A-8up=C1#5ba# zLr;=s{BWpYzwGJMr!G`+h{a<0F*0H(TBuS`p;lIIbn4}n>mU{Up6v^Wb+=&M679HT zUL0@K7@V2uotcC8Yq7()t`~+wPVd zPPS3;uM}Xfar;(=q}3w8T&6YBTsw{d z`LEXdsAkL!;3Ynm%NCU2(^fi{^;6+1$zr}XAvw;16nuqo4j8Bwal|qcODW6R4T=6~ z%35-06ne$VRTBJM#%a@)?6lTH(gO}f9U-_rg|Ixw?4paeE8?1mzf4 z;0gEIZPKrG&{pLb=PaS~W!jQ^EVRW*tHL3&IB0wDeX@8q z6L2*UTo&=tPt+=vnkk=<;G#I1quYjWh{z4y)&8ewg|F9o57Jzhuuq4-0J~lcy`Ppm zC=o)gHtcYe!XoGdfD@~sZIW>j?&y_J1x{H>8Q!3H2goP`9+aY9%o_U|(LMv*>}yA^xwVa{p?WM zADsPWvLboZ)H07K=A^CX!O-hp7N zALqfp(rb|&z%a-Iv`G7$U@P~t*DgZ+^X&C8D0zB=`wj{8_KX!5K^ubPKL}SQ-=LF@ zgGD~aLi`e-@1p&fkNR=wX%ALmue+&wncZ#Fk^I00yV0njEk;b{Y6;62^r?@dCpEwc zIpDyvg3K%M6mWRtZvoIfH3ShK0ONwFw7eqR#l-9m<4PA?`(AL3=up}2Kqn`0@bh|n zJcqGFtJIb!^D9aajNv8(?N9~LD(rSnUU19pPvDqc=zyIpNF)-KiOTYFK+ne>IMBQ2 zKm^9QfZt!|jC)Wa*Z=03>Ol9y{h;sW=sf6CJ?bmp6RCeys4fnc!!(zn`lr23u%8BI z2j{^D_xUg^K9m)gNTbI#OJn3iSRrI=MgwMrLG$mN1hathJoiJ=OoYR>WC_Z(AjYX{ zVbpK>zaYzDn(fCxMc+pu3dl5xCz@KDT0zoCgffnK=#C7b7-Y^UpVl$o&WIOyDXKXb zI^*ICHq1Nzs2QEpFFt?6%(W*>9Y52&<$qfzoqg1CD$PS}-rmddJZ)= zo^;ZzPxUPB`@X#w#!!-vyXT`lR-+1ga>&>L3i7mMw+*#EB^R!F`&pNeJ6hL|U! z*SRI&Qa(0BWwsHqri19(1i%-}x>Rs<4$-lViA`7>%dr z)7s+f=<*-qvC8sTZM>F<=JlV`GM(LG_ZgoQXGi$g_|OCHhJ<7T*@=8+qANjwTdj%Hj6VoUY)S#eZk3||qlC|Fc*Obm zb2u0P0t+URKa>?NaX8`FhRPINl$LZ3E}jb(;DY*Up$lc&X~~#{2e}+nYu)I9iA#y6 z-n!1JxAuFMg4m@8C{$x)Xx0HgrQEgwcGO-A*z@WB(t#bih68C}y+1&sXju?Z0)>p` zKZfQj0EqYiY>4?#=xo@KV(s{3%z#xcV%P;hHA=duL9b0iKNCjTny>-sFxH?ZmQOpV z#>ae5^Z8WNMB|**oM__LgQ&lyu?FS5BpNMR``EW(b{^Be{Njh^KNg)iXa)|Vnq1hQ z51+X7n3Y!mR$E57jmy{E`cJ9W&>w1%DSR2dsQ)%dDI9OI#1{t{oR*w@8w ziQ-$TwJzV|g_-T87(xOkEZAYg=2{Q!QWVjY_7^7?M|%|xIRIeoZ>4SxeHM6EX^7G1 zqqj#v1eYpku)!)B;%*DD1#(7+h$C%7+FF``aEi8w;YNYaW(i{7stRE^3v22!s0#(Ryy`*0b-v`|QU; z-7NfzefKo%xapcTg>iGJsGNB6h2d9&=Y>kOX5^-9PewaX=71Qrm0wl~8PRFX!I_xX zKwx23hNfYh5o&45AXNUJZ^h@3H~UqRG5J2Bg^%H{mbJDpt)-{6r@n4XMM;s?%+P(L zyfa88@aV%bFjJqwco|r@wI9t|>+~{Io;Me;_8ywrSfGb_sgZusRe!zsr0+d@+QmOu zeD#%Iy}i-=(|oq(SF4}7=ReF>@;YBR@7z0&E4%C)-51<uD2{DA9Be7C7=#MD2sP@XoM!F5U&zTPzBO!e)+1DV;6o%2gB#12oj z&Nl%m!}}z?4`HQg6rZR?^I0qyjcv?CBU}a%>EkZa=kyx!K;+r?j=CD>h*%Om_Q+aP zC)3|@^o;~X>|+aEZ_%N+b=E9m<*Y+y9WrOOKSK*XE<;;Ux{?c1cmDnTXP$ZQ{o~F! zeU16TH}?EFC(h>W_}=$+%viH#8H>TS#~Jzysdt0tp`{Ah@1(YXjY;EgcQ$NYsKsK@ zwfQU>Jtb-x_iSF=>V1(Mw7yI$dRgm!IoV^qhXV(T#-b;AZGiWE+DO3U*__FT&z-ek z_JV&BlhvSPF3eUm_*v;W=EQGHlXv&nw+;Var~d^yZS*E`Ve`(vzxPW^r$74hp0DS` zi~!-vz>a z=ex}JfxOL4@ziXW<;p77##937^|g{YouL+<(3^YSFK)ivr*qN zzW)5vRxOwt<@TEM_A<%aW3*Iq%xsnyUkLs#K=TvHrHea>0h_>()*icW_P&Z~4+~w|!~G@yD^3$DMdh_oPF5o7Uej zsryjn9I=J-e46%DGJi6zAw1gLh1^Hv!|K~&d`#zB1s|3!B-a>Dqp?emxK zTiBER-r`*Cw3cxm;R{L!N-RPnLh&@V)_Qr$nASUW%0c~oAKMkSrhMhVq$ayhLQeen zrYk;XojWWZ>?9rpLmnQq@Qh=khmIhgn}(7hHswcumeYHz|5tLI;ly71&m*>-ijxDi z*#mZ-PA=pQzU|KA;XX>}1=KQ-Uphsbp}mvLKM0KmJQSadTKmWjID0rqM?>54!ngp1 z5}b$lqo#A?k!7pQ?>zeGtL!xMYKnJQX9-J3 z4r3L*S~8J~G+5=<5|u|(x)Q-@A`&?zVx1;Ne+_oa{TiJmdi_`Pvd8^ua+2#UoNv(; zE+iMEi{Cg1d8OO+ReLVZFXa?*O=K>mgGvDstenqLXID0URP!<=oTnoFj&B z;Jyg5>m42KXTgY9N1>kK=ngFs4u&JaZMfCf!$JMLJj}KnT}N|%SLkSVulcbt>7%0u z4R}Lj2f#(7k+)-#S8Jj<7ae8CvFps2UVY7cnO$v$pLxN2J{KJgCrLxust?R7*%qmz zn5Q)^I$iSY2sSxcGM$B^NA&BFuw)$F_t1=E9K*xtWU|%b7@ZTsvFPS}59fH)WUUh3 zq3eQvFTb;Xo8rO;@;2xE)NGgKycdwmC=m|@2nD9Y#k8qpoX_Je+1Z

    K)3$5$Lof8o>oqSxlZQ1|Sz6TWaM?w}B8!5u2m)TYdfCELLdZocc;W30ppY4Xzs@l&woq*P17~52upA;n9AUKGS>7{v zguQD$QA$Qo%92F~?mr(FQGZ(5gR7_z6EXj^o3RM}L)iE9Pew=d9Vodw^vo|GJ@1Ic z1DQEr{KC;gM=tAa8r$VW)we>7?8lDEeCC3#uG#5^r5AN{%n>@DXvd0iOqcG-@*k#f z&n%7>DP)guQKrzPi zjsYVp>^?+eVOkOSFmmz@UX3o{ygFMtkR`I<-BcVYQZQc&Hv?7O=(x-ILOjURg6rzQ zb*;JHoeg^M=_Kz8Oc=0kMLsLEW^9>%0V7z}<$Dwy2*0g1r=JJ0;2}6rJ8=5czS;e= z-B-*#!K}l`a;E!_0n>xtT7a?OTTPlZ0_H!#XJH|uk;g)|)}S;Gk=?_yFyB>zCII26 z8eTI#Hcp8|qQh~R&`uJ_Qq`nv$F$6*g}#Dc<0y9NSc541mu69id#ToAeGtduCxp%7 zB@@>gP9_}-=1(<50A%%&1xsen9GW+Eo}VBn9lIc))I4WN#2%82{G?Ay!?BP}tk<{E zA!x_+T^wx4j90SgCdudV4NGou;U9-C^r%fA8Q(`aO}K*57(*i&gZjM4bUjBbKUpxm zAFJ;{)Y^%+F|hZCdvJ$R>#che@If&*BWGf^^l~;nY|-aLf@oVoP^GpNB7q|5`zUV0 zR2^tPN;lz*{iqiS?#DIwrm$2EdvCn|p6GaM5705%V4=gZe|MR(mCeb*NIP=7g=D2` zYjjw0i&bCE8hnk-Gu`>W_3vTiJ} zb?55D;+(JMNe^iy^Rj*B=%PzS`OxEFFg|(DvJic_D%kVHQCfTMS7$R4DVH>2i*UXJ-NsWyD?X#W>KJ(E zX-Cu5G2yVwZpCWJCe!x!%_GD(Wg4vNbf`K_2P^D^Ji`2sa?U{>q0duZtAR)O_HG^_ zse&=Um#pToz%>FRncz8kNBC;P{@Pc1O;}RZhWB&&G?c*1hq_+?VHH}0x-R2~X{hl(-pPEQ?rF4x`UkekBrc0q2E|JTK3dSip!w z{y>*Z^M(%gK>kz=h>;cp+;2Pve4H}4c=6!pZ@h6VNC=O7ZPC!IC6|2nj4yu6vW*Yn zu%9a}mbPiJsg&cbS~=dW@J_&!8eIn~62e%HpbW;4RC0#oaZII9nJaO;Lb&qzl#xOhDLMX! zD^~pQ_~XBK%+va(c0&1wOIEL5a?q+(`LUxuz3l2^j`=SA|8G3{Y08ymuAa$GSg`NO zC-1x9MB7JH&T%!Hd_>Z_S-jFGA@Mz`MxtwAaK&S4eBF44_-0s+IX!N^exfli<@g&4 z^g)hDkMBl<=Vp%K;uD^mIrHYso2&WV{`bIhMp{J`~}JMBk5I_-1U%U@+-eV0CN$rNJ zR|Xp%cBm|=$FR7x0x4izUl~6fib7fY?}s_`)qu%Th=Ks-ZjObMjX0JkYm59^5QrnI zH|S#MG)f6J7i-h0K2o_ zjZsMklmH*E$=05flkppeujxSPkt~T|Nn=}TJ1u@ORvZ4L6I9dblz#;fPlLFP6`Cc# z?bmzNrgMRL4?AGL`EzMDy<2YYLG^@Tyz|7rAznctrbCT^#V20A{`l{F=lJ!PUpTL~ zeV={Wd*_XXy+{AH>b&z-tvl!3wLNp^_H@sk>+s3W=Cb+#>8UKNv`Z2A;1FJ24>l69 z9H&ReS3n0}!zFWf{!jRd2Nw-t#$@DsKDZSI)fS z=<18V-;J_0)d7mGp~$)4wCdKB5wmWsx}jRN>ed3+^tCu^`kJjPrd8AD`n{DrGFDAr z3(ZnZIG}`|!TEkQeUUL2qm6-f9IzQvO`lmWQ=t9~4f_mBxMeYysiOYOhLx-SEXUIv za<2L_=Yt%M;@xBc3pjF5mVefC(b(bnZ%}_`#k-~Vvm~V}f}Qa~T--ez5O+qfb39ED z_laIRpSHn{C<_OBoqzP&pmd>8Eer(S$|iN;RPPHx`llS--efr!vWLXk}^E0He~^Sp&>!1{1xZ^0KM~FZ3W0 zn|9smSb|0xyrF=T=@Ge9Uhb-+klYZEX5& zZ0NCF_p2W~wCTQPR@bNB_UU8x?M^9Grmz=o{nA})>wj?Z;TJ94@cehadgIF8k_nAV zO(C2-Zup&R^q=W}TmQ%HCvE7v|IhEaZg=v%x4(1Bec}!tyAJi=0)7Xfr@{KX5QN20 zv|=19Dq6kR3Ct1$n3RVFU@lrj;9PXjqJx$!&IMGFz_gcuO3uB#3Rs3thyNUKL*qp6 z1D;m{7;h;I<9JbgGhnL}A)_^$VE?yaOc&X`3gb6P?b$H3&pic=j(Noc z+QAv!7N}o$jtA&6AcohWL7DXiZvKncpa}_o3p%IB@;d62!sklBvZclh_A_`9C27>u z)-Y+xq?%N13Y}`kK|EKp>L?Gpq~wAAa_YvwYWfDyfdUN~ z>p#AcrM6yr+i+buYtvtwP`B##10Q|%na3}^x_5pmeb95( zF}qpEN84HY;_X*&`S?=M7>5yfnPAi`z3qk)k{crG<~WP62)gZ9#}&mij`Ao1Sm9@F z*r8I#@4XyEcBKxrzCd8o@u&|(?Z*v>)~C}1({yvXIn(5Ythf^whAj1HC+f8ptqQpZIdE=2O%$Uxoc?&?6b zSm&b$7Lq>=TA0m_a??U@AQD(C6&1yY$FX?)ggEI4qJ}^#;RAH9qy)6VoZs}4fp>Ky zR-=W7;9sc-8!C#g%91JGw-7N5pdMK3eki5sk=*rMGNQ?L3UEFG>^^Hispf}xKU|# zi5NmZ>Efc2Vk+*5t3_z?<5r`#?iepj&T%f2bB~tWDr83BuXE-XTR=}yVXn?8u-Zom z*l#dQfDEYuGbt>W3=lD=!X_B&%hzY3Fw&37-Cd#({v#3pjuJK)7lMA$Of9|wgWcHkh#!@f9hJ^Me11FJ&5AP(56R)2~@NC^Ml z+mizKt4IVrDM(+oNnLDED{4i~lM53AXk{IPgD{ROemt_NBr0P9afF?PaJcINoE5+& z+j_U<`e4#tZ*Wsfd%NJL_OAA>&W?0jx~;{>SrPQwFLfn^3IX8`=A$}7O=fW)l=HN1*)Wc;?T#C$*&JNpW}O840^vw*QFlUT3m#wAD%-pc;7*nr2*eH0kFSJYw79~=pX3ooYplhokOSw zMZd++&bp9-gy3LqtIhFgvpA*jd!OBmlpUeX)bz11|Q%H0s~;>^cC(H6MO&ZOL{{21R(x?v7Cy z&E?*n7=@4=>B#DXLB6sh(Fa4|b_Uy|m3Fddd`7bzi*f{;T`=Iah^q0}sU~LW9u!44 zHDpn`F`VT)qttNtAKZiyd^t&uJz8#)$c(^W=R{(=!njhHQ((0Znb>a-Y5{!`gjk@G znvQnT3jZXP_%+ffOfi{U?K28ePMu%N4DzpW+0bp!&s=tmR@Z?(d{ZH3htM|zazc<% zcu~96iEk=sC`#gC6H$TLk}ryueB-^u#C!1#D|XLCqJ}+NFUWy;Y5X~i$TVB{XhO4HVitmVp;9p2um7X(2`iO0?bC%I3VvEUkUKAEjwCryqQym z9Vro)FW0j^=(C>}=+iPS2L4*wTX6mIk8Fu9ONHGkW;2e0Xu8>M!BGe!BE)6JtxY^E z;r!k$&1B2n{`5>qv{+Hq2(p195fsnaS1?~p1bSTHg4%Ah<$SvjHbmx%2H6jTt;r~IEaDk5KE&a zHkM2~eU~Yx+1YL^b)aZbNNKvU$A-~C)Wuf5+ItL(Z2syg@FichOjf(k!feTG8(21n zc3EGfnHf<(vFu)^_4%yup$2=EC6hCzM9wFRgX(sY#ba{($mcrRfE425*hE+BkN=l1 zmLn67`h*k|o!z`UNFk7W^Tp5;gzTn%X+$eRFLSq-wFS|{XoPGCa3=Ii!D~&x1%eex zBzENaf)5+Qm+IY70H)CDDRK-~cstzSd!9Vz=^|1PA*|lhJES0P{!doF5N_K>&*nJl zWplj}GbTsi#fZR*5smUXzaSDb@FJ(g%)iJ7RU(bGtJq$sf|k^5d(;+TT>16+tO$VP zWm-~rA<~#b*4^3O){+%Q6curW@373(W27LO^}jk=ixfh!I?2&a#FBUB-6cCeR*kGc zek^rowyk)l31dZbksn*&S)1vQAM5w5*My-s1oC6;M}5#>TP{NXXnQ3;mb6YmxRa${ z2;31E_PoaP{LwqqNrrtmm6eP9xk~O`Zf`@Ekpb8qgAjjIwOz~q+=d<__Fvl_HQPB% zL^K(pVq4op9E&+gvM{(g3AE(Lg=;I62yR&NeLd-^O;f!W{h*dS41uhcB!$uRm8=@; zLRcGlF1vi%;+6M>ER=S;Wg%7N8O^JT8T#*_1+vXCLc}-9wmkF6aK5KFNyHKAHwm{C(U6IH)h&k zJj7r{pjkP(+fhueAZh|Hv2oLt_bYzLK@W|reFecz21V=KcQ#{M|D1t2o$cwqramuA z1+}j$+>K3)uH?LxNkMeoVD~clw$Z<&Am+9?I+%Rl(Xqt#qm^?!c5^>kpBs}H(}5lm zktpU+;kYLZ;-EDB<9RaO&=Lf9MqeOBWN(>FPr^DBk~xOufhijq$-s;)=+ zzlepnTot}dI)+6ycVf0=-Y@yFCFWW9tntBN7C$BlbJM3H&IlvluNEk%b1nC{=V-(9IV>IWymXfCd=%?Ck`AybVUN?N(H|}G}pZr*V{l1HDUsX4OJ@?4@cOA9- z&I=!ZCMu0ay3bnifPUT1PxNaZJm##P4;1Mm{kNBXyyfcc7wfM|%I=>!VAL$V=7th) zR!!m7wVq>ga7EqV3h=NEw-#!#v>p^yxi^)R(u_ z9Cf}E=A)(@cy0=@zPj$3hmQzHw=3a;PSUS)z;+oDX9e*u(`uW*DZ&!n6ItR2 z+ZU24%&c`QC|E}9h!4f(-bC%AspGm?xgW0hJ>82S;lLm?+MzjtE5FB$=$!pHMd#W$ zuI$G>@b-Q|cwh)u6rd}Js%DPXLXZA0VCp&}HdhE!J4bCy0Z)35g{Nle4{kiApbM!^ z{x}a<{M2mWX9YKLaOZfh2UFC_t2xl40^Hg^sbk@MEW{7W7e^Kr0`RVK?t5g?dAr{X z{|dgx$HYut)`Oewp2U)+Kh(F^ zKgCwR^WSXsljr=`H78!Hzp-PN{_@3Ve*16d1ERyn^YqK6&bCf)_em>X zdEt}q{*(^6{E)}=H`C4c|L0eMh5wlM$8W4$Ffy%-pQmp0arvU-*y_K~Vau65S{A`+ z_3hurZaFU057jGi%6+tXjc6;KoFqd5FT!sMlax)8B13(nLZb1cyoPL9LYh$u4p_Ader9RMl|;Xbhm z*|F&71n-PZW@K`ZWx&a-yR)@9Q4dK5C_5445DR7 zHKsL(1Re*0GgUd{g13(7Ijr&5ZL4Oixt(dC!3s`;w}1wxe2F#wr(zW0I z`W=iN)w$|V^^5l(SlxDA-&f{6$k>0e<1D)T%9`WPf9Sgx{N^;aY{kaIwOQvG^m&!R zLmkq~cXu?%QHku-GRXNsFH5N*DWsn2I5Ke-?@8=*1K<{w!s;R4)e;E7#hBU4;nDK2 zGXE}C!si=}tnzyS@2oe0Nfd^xpW^0m(IVWW9S6uQ9#8j0(ES)?s~Ir;U{j_b&+D&0 zUV8Xf*?+&Tu04mbRVU??0hxnKPC4bm8*iLBfBp~>=|~=s%tEf~WH9lC3vVw0q~M)F zR~5Pzp<4?-`l>t09UJqLa4sqFobdfB;v{;GvOideYkc=J#2Q4Z^AqDwT$~* zXcM7cg0Pj>xEXvUmk|FPx|vnKTIg*07pwx-oX3kgD_6-DKW`K=l=F0l)Wa6qQk1w` zlTekmxtPTnDyZDt?JG}>-q0=|E=l9LS?{rVKJbf3FwZOWqAVGmy(JBGGoDjA)hnolnk6izuw6sktpWCiVG^zJnGh=y}n_OWO(XIPScIQZxGX!LqBe6{zx zJ?)cW8ajQozWv_vJY+j`sHdCrO^`6oo^ITiQ{WlHjT5l%8+^F`Y~MF|=O;dm-+)&U zpZJXdvu5cXH`JOWl^Va1@78^2-|UtkPup;8&Gl{$P&;LX12pKv24u&W-{KG93b=6# zMjB34u5`u~d;uNaV)${-AALB|2Us^s-O}q?Q5E?f8<-R_d&iIqkh^iY6H>Sy`Nb1i zD`2dZbR#&yN+!zrG{4?CpQdYk&Zj-^&i$bKwG?oekV-PCwK>_H>Yh|rTT@;dk4lX! zL4HWqm>MYKtz}K2L@f*fXwj>?Y8+#(Hiqh@_v*XL(YWwpPdmeNm)EqHPdR+#>sCX< z(TAUZi=Bx!hK|OD^3ENf+psjOY>$SOgsZzD*~!R#h|G@GBZAXQzTYZb9;+Es8j5rJ zevRwC@coAJ`F^vXv`O6U(lwq0CJXp}{U0<)dqPv1kvp-ssTY@%+KMtbA5ttOe7`PO zJ3YYY6&XN?ob%ji?MF_o=q{Un!UAd|dHn4QEvP%GoE?Ad!$-uTe^=!ROP22Zc6Lii z0doX4B{=&+$5HYWS^xNI*km0fXD=LmI64e?Q2b#wE7Gd#iTX<6&gh*oxh@ouCb19_+e9Fu zQ&PfGSb^Qc#jX|Cp#h~5=2RnOK%UXiN3`CRK@h z1jZ4|9)9ngXK#7B`}rTS{l>dmp(@kY?lb?8{TFmEcTzKC_T~xC+<5)3Hm`d8KiB}P z@peenf7)}*u?NqdaZp61a4y%Gk!{iAcuY9A&RjDlotfupTcB8K(ED^=omt@Fy{R)< zS)y>iM&%P$ysrlX+2%?3OLA;Q7)rABPJI?Y~7skwgxthOm z8>wi4r8BmQDA4$J7G}I~qY2kuWf4>YO@}I{KNquTxSWNe^a~0+MEacZi|YP`pzira zp*8%En9yfFPJaaCFm;xko{e0Qs4$@!Vr!JJ7cG+MCQ=+z;vq>|!*ABKpRTMYkp^o& zx0vUwx|h+tW}2<3#QXW3#`xDlFQ79aSY@g@zZWR@q)gr>J7<>OLS*EF{}3lAXW4P&<`tE-?DqTH%zBd>UKSw;PvLqM44Y2MPP;V?bFn zYW**M!zGFt;)_s2ZN8jDoAI3j%jq0F?(g>&o`&Toc_ixve(xhd%pcSH6ugg1GP_L`mt=Eu zzQs?lWxc}`Y#lv4!f@%C)-!cz5W|ggin#1z5Pz-d6mfP0u#g6I%DRzVI({RR)|8{GBzU4oELAZqSG<=0qWJtGbS?2z8#?}!gu*53I9YSyhkRtxxy9q- zP|MN^H2N85(KX4!skxURyV+i_cs#}{*#DDLG##Zc?|KyuR%1kivq#%3*HV;X zFk`b|CG%gYXK9X?Xfv|c3wkB>3mLm^agNm{!J^X>llI$Z?i_S_n%z4ax)}3Q8XG3m zR8|-hfx20Dc4sys)hfml{AuDg#BV~$3_%R zcf;}8*WdVo{_Jmmcd~xIGBC2(J5DH)+?1OnJ7nNGN)D$ zF%cvEy~y=<@u!gEQ8M&|R*fQ{9F1=xvmDplO+dkFD8X1v#+W==hROp44KvUHT*;9L zPaZoVOm0-#!VJk;&SPIb%lX21oL?xochvC;+U-8RuK;0L8*uC;-^<$YviZ4R2JXUP zVzJN)=yd3B=c~9g#o8BA2fU|>Na0ABGN9lgp`GP@8KVl5Wm@^1S<{C4dQ*)R&7}WJ zi;L>pTA07)uVEocRGp-nGGc%rT()iSuqGG=SHZu|$5_eX_x0CmCLVwDSGVea{P9m% z>fZBya@@pH))q=BqrYWo)_<$M?KeNtUwJ0M8YaH@X1`9<=1uT>Cwyt{TI6)&_B9t`S0&r|E-s=zV5YmcwTQv%YuV_W$AvBKsahV( zD#!qX#(N8W3%K$z{tIl-vq*@XJ?$sxuN--#G)Ol9L`(nj>3_TBhEHUg0;Zqbt?MKs zD97z1eHyP~U&pm{NJ`=wZfW%60XRGYk%3Yo8QTC}01OPoY$9z}(5L z*b?7sbP3;Un=~Xnez&n*k+;=u?z%Wy8i zUdrGPB;y0%=43~E0`cd*p7x=Rp-j4=Ez#x%ocjXk9t3Iv&gsicINi}f0D8Q)xnMpB z%evW6(EKOC968Y+L5I{!8R{`F9iuI0ObubGquN$d90@V0s$7FyLGq91w$dLsaZAvE_-RN?MA^g#0b&L;$Gx@WYD=;tKe(CT6n)E1T)1!GVa#TTL znVoZNfwnhrM+zwj?v&#(6*mv!LM3A6t$Cg(KTn7B8EuKlUom6Sn!#K~wS`+Z8Df`` zH<~l)WTIgYaMtG>eG}+#@)CPGV%|emS(6dXa~{mk*(ROi)?zKD7IUT@yN_yMuTIO@ zdB!HneV;^z04X9r6%<7Du(7Hgc9EHOoCojnr_S1|^F|-C?2tZbxpoA{eWnSX$8PA9 z9&j+KZ}OZ{#3_yHH#%)+dqZEM&rJmZPn+G~CkUalKV>!f$wv}9pU!UZW7398c12V} zc9GaZErA$pWblGV(d-r3RVU-xn`FV*`vJ@ClJBsgGhI#=)0Q)JX@+VG3~mQ^7sra= zZ>A37{--o}J(VIEoyw#jqy5xsAoB`pwNDACTBqCny7L`Ys3Nmhw~5oU4Y_uuo8Nnj z?B;MLdAs}A*<#zYUA1nkxSGE-^}*+;BD+4^}@)o8Vu{cok3}(c>*{3M6(io&u*lGIylPUF2l& zVeBKrhR8_Yv#FDuBj{KZi=rZehDez(D}qI#u7G*(N&T?zR@C#$UZ!FJ-SExz9+72H zXmW4xvx2N*n93{=eJ)h)8+|~(fPJ2o<|WUXmtJBAvg`DfbPq6K7S}A84Qt03vdcVn z#D*5&6j?za%LT2yacauDNC!PN8DvajAvUBxlK0pjz3{>#Y>U2{-+e~6Sv=V+9ibh@ zPfUh2+0;gWJ|9z&@}f+De?zl+=SvQ=n(ld{JFK$~O5d?LVgfj#*Cb;Y)>4s*{$Cz? zE+z*9$sTp3*#;5f$^ebMhw9` zoroT)f!N|le(inoQOc*G?AAWSCnU!am?z&-moy9Ab$ulHEK`yfOL10=*_{GXYssIS z#WFPeg~OKD%uW zGLP){jA`E!O~950yPUYSLi7pis=|5UeTx^`q)j$2Q0}00SMg9Wzo57b@;cN#n9{*) zFX2|8#--+e{HpS`09TcryX>oq=?6m3XhGMoT)Q9!xjcj{^tNU;PwhUfq4b2Q0DsV) z+*ZAt{Op6)Jq;dXtCyL1U{A+2+lu7^lIqv~*96>A zV-;}nud)Rkw*%DIFn^Qiph;?#HfZb9n7?UJ0&J!dbMR)dHF(pIZ+`ZZQE-{bkdn-_ zqKSQTLsLUjD$8LQM4%|BnNDOPX{rbhLKg$l-GG01QueN7E4zogTaFB@Q(D$2aFJgvOVzxTmF&$ zO?EQrz1?rBU6|!3U@<7&rVKj z=a5HpmS2$Nh)xiZp`N$1IYUIY$AI;nfER?|kl%cwmlw?4I^3{J0S-6_i~56cU>I6oAYIDq?z^X zTDrA`fG%c(H2C`XgGd$2$3JEVsl(r|rvextvcvK3oC+H>ev8U+Gk(h}$K77hh`Vz9 zUlhMJ{a~igFHro<*f<%7*f=50&(RkTaDabF{LJtLIAYS5+7@!o_;N-JG5}#3$0KVH z$E(X9Uv@tnB)l8|EdQ(FWy_un7V~My0k@nnp8)6IgKO3v#C(WP-^D$up36XyXMy}? zY9|&#PZesTBC;!xZe*y5lNl^z51&%ux7BMlC6^8G7HDcDJ(yxV186o=YTjo1-EoKh z$V*7;c`UR5c@}5i*P{PtA3Iq7>Bw8`gRKIW4ysiyLt3SW&p%s&hKZ=x|xO(U5 z5|cE1{{wu#RvOmgr7RpOV+#6uiSJJaka#1qjf;`RCMhc-D$-roEylK}yjiP*?hYeS zc%9F$%lJ;wD>a=e!~aF8D;x6a6J^PPz5%1ATkLT%Q+}l1rT^}|_gp)bKK%5nsk$bMz|Ga zLuQXrb2sU-Ft)kRJ*h`v+*o$@9to5#+wLHO4d`iIBv+89DgIl9$xT*eOuGA2nY zvavZ4ox+%cdXaVZ_QEDwYwaHm{2D(k(<;i*dA7E?Y*P6of#--Ho}KNA+&Hf{6rUUS z(f1s8ZCTI(_FgNcq%*Q9p^5IDkx&#}>O3S7bCX2Me$Yi5uE&;XH4`UDQp1!9sfj7- zrz>crC@+mTgQGt5GQ~Wl58cR~v!aImr<{hSN!vwUX0=KBQ$SK3FgTDbT*MRL#SgKt z`v;_GiWaRFG>T=JI3yM=QA5Ndlz)!2%%Y|IvB`_Z;TQ}N2KiASA&>s8l2srsN|^s%e zf2EQS>T4lW!Y2StlG1K0LNOJ1!YG-P9*5k|{j`=uHWh#Q#A=qlSO3+ojOvl!jDG*wOV+m^x9^YcVx2u-JmRP9pnv>>9drMRv$}Wg zeEpZZzpR|hO5Xn~OMd;mZ@s0zL$U~c8onQyjy}-?(md%qT5U5VdkZ8x`iv=nDT4e2 z(=cF4kV2Zfkn_mMi$n}?QHb9OT(S+RYm3$_VW=PnSKd{CnN~M6$fQ{_2ImdU>uAfQ z44|RThN5)&ftECtglPRdne}Bu@t{Ib{L(fwgKn6Zu33IQ3kJ}i-m~T5WkzG@b-uu* zTG{VMW*9U&AU&&%pNuTMVit~appw&p2o-Z>P%sdVMAtefmQwbdK&5b|XoW*~z+4$~ ziJNqDEkj2fw^z&~Mmr7g?JiC|-<-?Pq*n_O$P9&?!l zl}>muKAUT~;1S}G*)ylZRm5lScz9%waO2K+8zX{#Xor`^h9PyQ9b&TZb1=uxEGB#L zLk3L;MbM+eh^k_^qm3L}iU>t(i!CAPqFAPoaTsKYkaX4(H!gC036joQ5k%6V&b*I{ zx@kkbv-)Obv)G=9I_D*Rj3V!K!#o`u0>iJ{M!LaYu_2DX8PXG4nJxF^MMHyf0*j0h1N(jTfZN*IT8Fsm#ca_OIF#KwcAfB0Zg04b_O zv-FQc%6asUYn26_Hq1nhb!0l4x@QaL zjRENeuWTbR)j_SYH^abkqa>Ha5xmjkT;#?`u7&6<=J-$$VWs^G%rf5ib!@0Ojwk<* z4GrN3?fgrj|D^&rh(wX5#RHK+l?8BMFA9tU6Yv4n3bp?)FpVj9e{49Aw~j0v8wS+v zBCnrG+b~%152zuklrmZ>jQ%5Vd@_dQ9%BU8Dexkqv{G7Q}z0zox%A(xv?Dru*?V+%vZ!_egv* zg}8iC828LnjU3?#q?9p~_Ur&RP3c&^4UxA3i-Cr&;95^5uIj3sk5jT%QCW34-#ed% z;J0_ho86OrmcO~vIDL<{>tT2PCfj|v0bAUtwO+&&qihjsaKot4k{P*`;WIa~iCf_M zk)(tHW%7N-BJ~2&0}0%f@c;ZEu5V*QEW@VhzsiEVdik^8``)wqX>RCaPpBCq*f+yt zHx7G>O=3~04)-C_LM;G(i+78$m^uuIBf^0lT6w!|XxyYVIi9tK3gA?(C4_`=nRIG^ z`*h37;yF)Ni79POnE|K!i0CT&l)Ja$hF;NLbJRH|p}CK{>xXYW&+p?)H-x4W)d@O( z3cig5;UW%yc@a9HmNU53;OLjxFldOE7AnH;gW5Sk^i8S$GL<}Fpd4pcQ9f5Hiv_sv zpdLdqmVe!FZ-E!!)gppX*Kc`9cnOxAq+ctXS;Q6O!@~Z67usKkw4b)Xu|GAz!cqeZ zg~QH$k`(0rDc}TtEW@XQc9;M&?U?uZ%)R3rfcDuvLAt)Yy=M8j`Hta&w{f1`e~vv0 zI!jBJ-G!(FZ-7cEnv=0(BXv+TMF8|e!-x@tLMQWXo}_gX-iMF~fc^#iQ3xh_5JG~# zh=&%e=zZ36aKTaVBQ2%Vsmi7l4=ISrfQHAC6I(f=HC3gulx&0(lgqQJvTc?)JM4^U zXFPElYrjwb*`K%T&k)Jka!tSZn(Jr0!T!cBd+aeiIeNYRCY#FUvZbK9dq+0n)4=Vo zbSeCjN-}o;yTUBQ2ZQkLDi{v@s7&!G(c9*+=Cj$?ewMr+~F zPg$G(G8YB<8?5cNE#H4m-l$ymi6nn|=H)EK>Xqk54&g$WNoz(QR&RnoszKT)_#SbQ zuvCHnLLVUw|3fzZ2fWrK7V1a{qe-nKAq>VCkMN-UpVy4mY4t!24nmLM#Mw_Q(_&Sr zvWj#su~NkV>5fp9=!ZPOMY4Gk4*WF@hjedc^3SgQ-t%;z%7NE^@xJ~i>V_PyMwjE~ z-x~dhz^h!g<6&j`SH6jImH!T=M}A*{!0cn6 zx4fpKyy1|Zt-Es{f2Y~^8BL)taKoXX?g0-Py5|(GdrS?_bq_ila4o-Ab&tIJT+)Z} zlfeg*rBk$cHKrSp#X(+r5)J8S1de-jFk|Rmp`J)&=3|GD+E>HsW_p-~aijEm2m?W@ zs;VYeO)9VCEEA)?$9}rs8ermMz)ZD#T)z4~w9)0tlH}cA^`1|KU~-%f`^J&kCfa&9 z&-pkUa5SE7^Us~n>eu6QKK;AaHOlvZYQ}L}BD?K}{Co!ke+|-9ZGg@OYZ1do!ay1Y zD1FdXT{UT9RYP@yVS?o_A69n-xA{GI~N3>Ly#K3jN-pkQI5I^Y3W46Gn@yqF88r zcsyRDzzDD5Go78HVjFAwmKDPPR8pNGdvSL5ci3HuGyx_Rebs2PkAJ6ED#k1E8ibee zN6d5n8vbh#Ib*cY3~A`d&kU>CC?LMx-_ z)+Poc5aTslOCTp%NDZy_T}qsYksbJrXf$gLM;QMGHN(=T5Xq*K4O1r7VKgK9v^faZ zM3$3JnJ-uz+^hd4CwMHL-Vx6Rlq}%Hd3>TYETGf?lu2k&iqI0VA*VAg?^Xh@@(Gj> zH`#uKkR6gyM`=Ea6OyzTBaVq5FUWO>(H+@a2ac}*-L)Dxu8>v>j%<)ZNKwIse=WMY zxthaOSw=fttdqvm;*N^nYwU;tFHqSqkmDZO zzNhb{J~i}S!Tiqml0B}>K-^!EiplE(HA88gimYr_Lr92{#kS#(BLT;^T^uTkqdOz# z>4+h9tSbV(GR?XY@DJVVyaC9&&*$I6f0^)+l@%mDU=|Z~<%bWI3F8ZFzmbLX?x;li zTi*Q>ylx`ma4(bO z(L3bv;H4_5Lu)OMsYv&!z+X`~#~5z+0NG0NL?FV>@Udt_Lf7LEh2!A0YZ@HF{R$yA zalu8fjSZmXhI-@%*0%hD&Z#Y0<;2F5M(?v~KRKY{HMk{uHUGT*uHGW*CRn=5ezHW7* zCSmTI`l9*o+r=DR*=I0QBYn>jn`znZ^6vA1yO~bU?Hb1=~c~Hu`Gn~ z(nay8V8*Cv0#M;}b(PWR0vjB?!_mir?{L)7uY$`K`4-fJDzJKnwEhYq1H$-j7~chC zAP-!yk)&}Y#YHeZgl0GSNbyv3;?om7<(mBKzjKvkD$@0%@5@sR8DA$|CZN>lE82x0 z*H+(DdedNwddsAAKL?Mhr47PZ91g8=qB?+y=_GjPzvGCeb-l>f%HF{&VkaJ!ene;G zx_G>_P+Yoe_+Gf=BWIxu3))b@mE7)EWU*UAHU>8PuW)Q=^nGg^Z1;1c4|AKp#IpGz zx7bPu1$b5)`$~EKHYKDbTUn-zk}q-0Pd+G}T-trYF{-rl+IRi|eQ@e}l1bkjeOPJ6 zc}gIATgW_uS_XBmX>bI<1twlYDM)694bcI3OIc|;Uz6x;!6L{gLnA$Nf!Lz`6aZUOEhJ!B zMPpxCS!tZylZfOYvg7~;TA3%D87tU2>(`$)0i^$$O*Lskl}0~&cH|5LUO0(7>cE2u zyDABB#oYTcHp6|TzMJvcK-Pze_VV62Go*kUxdKjP-8rql`w(AbjCW4&{qacAR@Zeq zG&V!xj&H_$J8a|^NBorMa%k);-(@K9FC-hK!a+={2r{ZMJij5tIeuJK z#kdLMCvY)WRd(>?DG(xx#N6@ycPxCq}Aag_yYQKQZsXi9mjX5C2|$!+}2i>LtmojbR;a5 zF$CCzUDZE0gFD<2x~?3@6r>1LseE!ByZO1d*vI!@0l_j?f9ROUW9QF5f-RkS$o$d! zUcBy>r^CxXycmJV(^8bgF&}J1@pJ~sMVl?zE6l3+c zs`4tZLvMt!+e~cgI8W%a7@#a|Y7=Rb=k5 z4SN(pQ`bfttwYcQ6r47Npp2?QwwiP+@3i9*fsws*1lZ`vjR-x?^-~!lhlp1bel;P?t-1}ofJJgVdO*fOoQqMV#Q<&!HWi+d5T zLd0K+rQ-4mq+U(n{%D zqtsvn;Et}!h)Ea2^KhzGG7hF!NI^H-y%zFUmP2Y`L$GzWkmblXeK(OWK*@91$N}4vyf-RsZ zPJyd~^9%ygascoejyBK6gxk%tDdB7f90=F%8@R5Oj@M$=e#G_A$Oa;UH&ITxOf;dM zT-fmHgz@j-0(83avVqk)<^BQ+FT^zG?24MgY_Ef&$D~SeG>b}EJ>4Ar)xvj z{>ep-jU8tr+uT@TLJbR7pBs`&$;d+QMtG7YJXeUB9u6C@SUSLfAov)8HHw;+b3p;q zjGN_#GpyYFU-Am^Gv(kFW2?3(A)%HOqGYHne7OGN#L;W@E%HgDYuU+i(YI7cuh2!K zr^{C;8%7s?OHLTF1pZ$*edumYp)~G+6!Lrf^_`+SI%2f*=U#R?O zGN-$rZBSJtQQ0#EWfZg6tK3IS8#@{~h^~1ifVc%Jco_*k=g)0{> z9hw28lcLylq&GwW9RWM13Huo@rL{(U8;N67z|VJJnpgnFcxn7V1=aC0d%}E@X3(?0 z^9l0x$Rg5rM z&pMc~L-)N%X&iZ7X?*hL@4ZpRMn@_)E;#y-se?;7UQdOtpN>71BSv|PR#pc8hg31H z7?-jVhN~HhUL3a3n}t;RmfQ;jMIKKD$D5@^ishCkeNhV!lp?A^7?Z+YXbN3VIg|G8UO-Gn;U zcH9ATmN0h6z8wdF1}<%U{`w!hcIl~4-ODPgtD+SxJY_}%vz~_#>uMEXW-i?PRdzDb14KQqx;1mw6A#akvjMx%6 zm_?-1dW5@);cvAcrM=|~6=v6+2&p+S|>CvIA zt0#TOG3wPg%r@tBlWgeSA7P3w3EFyX67^cK4RzTlkV+>l&vNz9Y(~Xq5w(eY%YFN2 zGNps{D`r0WU{u=qqvH;iSuFGc+NLNE-VNP2ig%ab-8ID3n0Z!S#&x5NV7nxfea_Fs zr9Gr?O*%~uY|@gI&hMBS^OAOGTI&-qrEX~)@P&^V6-FISWkA~n5%^O){A>WlHmuObRMA ziT%fmrnJ$;(z$<^KE-%3Q=#@jpT6vX?6ss=51J3fnz0qD(x{*7!Zmw@=~Tc9FY7&v;-fFSAt+-4U=K z(m5pA`n4XhA5b7{$`Im+ieg__OhD!*M5<$Rquf+So{dD-h)OhBx@GiOgU2hR9<9@W zi3=1YSwjfuhe8`d1vr7`e!Dj?S=>WC-{1AAxQPlFkp3bVWbq*dC-Fo_0ZpimLvND_cY`Db<*K?iQ@|;gT^qtk zIMttfyMMI|6wi)FF0={9)`>*N^~ff5?mC4GS=`3eum^Tx`;aU&?5a*n*pG$qN8fW) zPnuL#I;nnA{glZTC(zy9D|(#gF|s)v3~Qc=O(nIK^IWF>`~&poC5Bw?)Y>eb081#= zr85{eWbZ|O+-DD?-{sd$+=E_%p8W>@cCCavFMS&wzL2Mnq&S4@l}&D3FDBP3ffH0& zTUt|I1xaDLdtH*k;qY|;hTUWNe6!(loJ$o$7rzN&t;XK0J1A0FCFG2d&x+qc5gH>E zFIVeN33ZMigdZ%ro%dfk{BA_G$a8IUae~4LQRN3*Uq}{A7I zJ~Q3?5qaSFF(svv$l668Dk?8PRZ$f$ima~j)J9feJ6w!YQBi$G_bXRzdA_%F>IXl5 zp2hSRZ~DUJKh$5k_A6&UfUwosmtJ<(8CRff*v?&_Du287;yb@S(xHrg<*LW88KGm+ zInalS&e08T6~@WJtvLY!npz@V<3RP~FtZLsYKcmx;1Q*YF;4VJelmK@JX!Wr%QQ3) z!&kcLfCD%qDJ!2d`jnF8J+HpC+bG@3b?^;#?cNu>5I`-}?suafz!#E|I3CIKZ{Sg& zPGC%0$?*x-1n_A7fb*;o_{6V_p^2X#9zi^q>B(@d&6w3Wq@sD7ChK3KALwtIh#dOx z$Is~>v$mU0zx;=+^}A2heE(bkGW;yQzDRpyO}=qGpS`DI5~9D6XgeplUQ9`2Q579KK{8^9er% z*vL}?h1qx>qqhv-TLUU|4$4*yUf?%cv$vk3_}FZpXI2~= zG>)3%+1O8;^m*5gnrL`k7^6JbqYt4v3?oTJ4!qAUTls^s=RyCSYo)Et=_KxMq9CWp zEXeU5p~7~bI;Nxe9IVAJ-TXomwnRfkf!_u(A>tI9Esn{C-$IrrP%Q_3rfeK#REXY2K(*s6qx|C zzHXu%QmGFiF;4@`zKBH0{wn911qeC~gpl8(tBI#6Xm2-Y;nCNh-K~3;lH0>?`#Wo`7`y?0Q5U|R>cZ{1 zY2xrHu6k(Cfr!8xtq1a;AxA-Y79NcGnm)L=&`JvOJ5b13E&cC7Qx(YL_c_O#yaLDX z3{INjJRxemH(NYG=I>;>IGQOp+1LrM?OQw#zR<9w%*K!nE zd(p)lrmfFW+2MvK$@jE&;W+Wo`vwm+NME(iOD61OFXOvw#>IIjT7~02$8}bf|4UDM zSf0~5s>+3q?XhIWQPz2h-6mSq%6><&e*CATof+ftDv)kb&Fr<`cri2KXq&NTWPxdM zSZpEcAz^lTPQU^nj~`W@nCgT}YIt?faDnG4r>$pv_HR80^7C&Wkwt!{OUsA$hyeOhbf4`T8WV%&c4aJ(8US&>S%HYYn%osE5_1&{azoLjanXkJ3#D6tDH zTz*jsxVcqCcujWloFBm0y;dfsGnEeZBF%J~1p5f_IjUTPk1+)Dr8I`@sB@x_P}H=sgewl-s2NfF$HjrCLD%&w6Ux|5Yf7(aF(a4!u5 zRHqb^ zPJj6>;)o-z2TMHkwUOUV|AW##9gOi1=^n!2o$3iVONM%6RH3ShqRoge;778v4QqfL zq4oh1^|hG^nWkoho)G~@S!<-4h!ZzxlZ9($V>XJLfy2+dbMmIof^lCq8TrSo_?QJP zqvtsoSv}!r+lTJ_D{a_h=`Wm^zW&ppo^rKE)HO3DD-j06i7Mm?v=a+G=@ z2F8Gk8#uEyBpZ^A2`9P50tg>6-|`YyEjQ!}P}6qfnqcwH|JeTNN|Sg0W&Cn8*8QK% z;opp0$bA#I3nLdJ913s7tdI~3InV%~I?#YyG6_=y4b2UhMxShSBEmcR+fUwE=^}Yg z!VO+7) zz#1HF0h**T(=sfTBF@eRibu&Pi_j$?Ja4J4zhs(<$bCKZi)Pn@eH^-mx&|!$;l2ax zIyPY1j`~yjaf3r=9c1)FP};nDht7wXH=VCvI!rs*aYEEy8Yd*j4$WTy)6xCMJ}lu7 zhwbU1wl6b6{8zMUgCYs zZj+Fodc#~Fdv)^JhWtE7+z{I0&Q6G-VIUOcP5xr14!!m5r$e~^hSZ!F0IlB8gdGhX zZ7rFmluH*NqfJ-NacA&Qm z@?FP8OB7X&3{zpe6ErS+-?&2gugZB#sMFf5&*1g^OGxH)z|{2-T&mi7y3>a6$E4U9 zJu})`QeEk;Y!%?073*tkKs!5c%z-ah#9()g?UEIP8XxiP7bb8JyPe};K$<4~2k8PgIBgIEaQb^Okrx!4PCCs8?_gM8`0_9gPsxwi_qmEfHWB-er|OYYE`i^rKow9Mi!9~9q*)Km`M~)xA)m^2?SUKA6L{ zO*%o)HVQnb^BSP&q-WPzFKCd z>zQ^{Y?sB?TTN+aa&-~Z7pEa72wX-MSehn7w_%Qg5Cql|C)v4|(C`uCX_|bcx|!Ma zEO|&wR?fx5LF_p0-wX;%dELlD_UfV&Ph7O{#1r!aG*(XxyL$iqSJQt`pJK>Lp%({H z(Q}-3>^N2w4`UJ`y66(IkZU9!M+&Wf{n4y_!d|h5qHjYEe znk_!V6lKokxn-GaHDnn9deMRVF3@IApXx8mf&iM?vaHIMWrm98(dx8N*Zkk}Dr<*) z+h&Tlk@t={XvtB_K6CJL`E$#cEg&9q)bCO58+|~(fPJ2o<_DMBi5iUq*>yU_>P=qOEVvw^Ej5oA#;hJH zh9ao0kp4*i6Gmb$yzmIyqC-}}SAbez(@)T_Quo;7>m>Z1s=OhM%+{~tr;A?p{b!M1pqjWJ({Uk z=(ZALeJqiQqzk!Jz^vbSgI%e=^7^0TFFntC^p^{q{WZ2+zu}1|US}J0bn!%g-Jeqo zDkknM#@UmUqywLPRK+;2h#JBGRaB{wLS|=?+2Z8m0%uRB#(sAAk}{D5OIIZGi>saN zD*ffx-_T!SSLl)7{9b=4|Is78ba1x(BmJA~WLutVv6%+m2c;Xeq86q`2a_`9df>u9 z6}$ZefoSAZ697^S+7^MwVhmcm~_4IUibv9>+diDf@ko0LlFicED zg;+!AJczJEekK~n&e3=7mPYlRY?J)>ed?i0*B%sm;L(i>3V^jizv1n-+4Aq-qBgd4 zHGShtYY+QO%+v|7ZUeS6rL9_ZGmFP)f@v>PBcL%gv?#-37~@BV@gU%fWTg<>M1YD= z2sjQ9#v|)c-R+_{&(&-yey5^aL81|msP_f7cycX;F(Y6-1G6u2DQoqEc94sipx2nc zO_LYqAF%J~pNx*^JJ|Qwp=W;i=y^vh9>~o3;unq{I&xWW)13T(S3l9Wf}ZzdM`b>9 zL08x8bi>k%Iy&ZXeN6faeTGP$HN*3E#a$xcAQV?8k2^q^x#JEFP8P$GibV>49qbhf zRG15*_Yq|nl>Eds9Hoh|*X{S7{`iy#uvid{G}W%5GP5y#Nefv$OBM?ho8 zV7BhN6S;L1>uV!Vx+B_0xpP#1h1@xJ;E&3)%)noB^Mu;ww&u3h7ApWq#aBTCfOP8! z6a*B(s6e4W;lm@@C*mLfo96@9R0KMo2o~anS|d!sUe8UPxr1vf09Ad8V&X4ZCOviv>KVKtMC47RZFIj^@6W zK8udVA`_hM)j$w4yCWKBFyxw8j|DMCIu$S~!OV3P-3V(dFgxr&K3O(GYU60fJn1zBg+CRlN?fo}5G&?U& z(Ez(g>W>=949?1bbztkyIc?*{{AY;#?({Pro4IoR-6v(8qeYsbP0cj%bKnYWXK#3p zK{sIc{U;LO2+BZT)f83}>CAtIfsa|=jEx&N7dXW+r=N7>CDT^Ue9W}T4P3O~M!!It z+mTM8c>-G$j=Ic2UwZ25Y-nj{abhG0Jyldy10%E9EXJym@I&RLt*Pw%FK-x{E{~j} zKg?1Ej`q8aUwmTTp>wWSxmI6L$?lv4JQ=>I=xv-==!|o;nH`xXaYhfxxmbp9)z#_7 zRv>}{wkoR$Tj9dT(|@(2Z`Q`oou|~Dt3Sdr1~CtQ2^?!5DYdnzy>PN zjw~-W@7{ds$tN*ZejQ5|^tTCaJMqqw^sZXAb&|f&Iar4I~&AOUp^oR3fH2n6A$Id+c zq@SNO-#DTr+rP!>H7#L=YAPX9a*I!jo-b22q#Hb$VS$(uj95{X7kOU>g(j~paG*c4 zdnf4!pp(Cd=kQ$SYVXBD5R|6b#1T%XjHJNqFRL(oiwo)or6lN%c@0Yb2Q?o zNe71#NN7&*$Y=JBw{EK%d9G^PAC=D9U*CQ2ue0|%x^?TQeED5Fb{ICFm2;hApEOp| z+$a2bL_}Hp^!4EF&3)#yMOqxThyg_>X(P9^pl>l7hK@9!Z1}lydQACh=Z1gDC*3<^ z<$ScD?>YgK(&w<r+>Mu z3j9N5yJ1L9&PM2Kez2KV>{A~QE`CeD_l`T*oR@B8LyzrxKz;w=P4_jkx<38Z(Zi6S z_QI`Sx@%p2LfQ2=JMR5=+1e+s(SN4@ZT;&Xp0uIw{y!@>-2TofTMa%OeYp5A14cd4 z&D!`jB!V|1`7M=1=R~tWw4Q|f3hKMb8XcOf6=W3(weh*jG~_p_;v$B2&xR&w@qJWF z=Kejrg1;d|L&7Et1ax-5k=2vvae_V`fb8I}$_!Nx&S7xiz=_WQ?0m%-;r96_)$M=A zjnmSjzd>kfb1B=*=8j%4|HJ$M9{F=s@jn`;UAEN58(oQbIgV^rv?LolStePgRF?NyS&Q;EmYPsE zeA_qfW67WVSbzP#i*H|5SFS$y$oY32wfxQtAAbhDNwl5aXRUZZzi#Iz`ZW(8b5_sD z^I`2H{kNBXyyfcc7wfNb7(ERb?FSf5glF$RwaT{ENwo^Xw&-w-nS$sTgo*^wixWl{ z+FuaFKwL)Mbu))X3Y~ULh-E+1>Uf4KX9~TCpxT)8TgDEkw@(8e;$UsD_9MO}t-#3| zQC@5SwautDkDD$)f#;ccKaP4KAuH|5s zTdx&Be(sUa?zhDW=|}VzR&TlFh6Nnjp|gMf&9^wHw*%~n+bx=md|67s9)C<5KM9=* zFd!gQ&M-FF1U15`u=o_DW6`Vz90r7AkPup+il&eP;CY@zCsgxE0n)u*7ZY~8 z)Em_vcrdR1IUJEkPIS{Tuf6-f%zX!7R8Bd zg4TUlY7DI@e_2lei>5Wz2S=!(N^ni*%MEfs!;tpqV!8S4#F{EVsjx*~Abbj3!6EwKdWB-3n( z$DV+?j@S}tFkv)dqptTtWycy5mWvEC0Mbp=vRo8-Q_v4dG@BE~w;3-Yt&yFNcGIQB zd4c=@*%VOJG-iQ$E7Rpjp&g=X%`NDn+mr=!TrZWiy_;1ud-Y|{@K5f(hXtO#@}Z8R z0u~65S-W}Dzb}03h-r_b-&pU)v!}nnX8*a1&3vx?>^{5xSflt^uU~#k}zfL{p0#zsjLLhT&+KhrUn!y;sCZpv~nzBQhf#yQ77 z)CjtG?Pd#_>c`yDuw>Ac_0r|g*$g?XKpn|j!IT^u?1pj}5R{`qhHV8XsJ4+g3CC2# zYJgaZXplNxip*Qfl|VmwrApJT-x=WoyT$G=e zmFaOw6_{gyPy(Y~O7Vo&n)Xq*rD%zDT=^8=8&7B!K=-A3U+H0?%S^KS=I89J80p=0 zvet+xMu-KiZ-a7Ji@7UQvvYbdr8EPyrWXE0>jI5}N{18ThUwBoY$3!#f|xcv2@{!V zlOjz>>%pIBAqH_(_z@Tol}z}?^6KgqpBs}~nA*6|Xf##nfTELRZ)TPR{~)g!TR1>_ z7^zh@vRiG=tInO4*E6ehavzW;b3A`_Gv{Y++s2mgH`#!(x#35)Fy-`fczE5T{Libd zK?67QPyx2#_Gi#2^i$Tt8ZeeEz{lBo4)NsHht48{LURa`CZRQq7Ry~*O7$YB4GA=jkh~BqtaY(AnX?QRQKMuYoX&rR` z$AAjz%!rPsGHhyDnFrfivP~s9r#lv#bRd&R2vgnYy8^7&}$ z6%CX&r6IiL&CeZ|o8a5CUK23%WL=761C?gE(p;Tt=yR@7tq9rh=#@Z)Ok&O#KzY_7X3PNwI7OJPa346S7$1i72&n1Pp4PJWm>$- zN&Hh{cJa8ptZ5Cz?Au1@(>c4&fVvv+gC9;ymHg$ z>=qJ29je{Z?OPT5`)*YDF==}ECHW2LXyhDGuX%>Hpz!|n6Hc5~RX)*cUhnwlY=Og6 z%5vm4_y^Zd%Wm+EIs6}QT@ZfBas9~$+m&y!4}1P5ymuYsr=NIlWe{Tn(1c#_0i@Tq z6o?D*2Lj^VJ(#yI^*j@97)JN8ANU97zkS-YG1)zHre9~9ebe)YWq+gC4?a0+|71}Y z_sD(*Ix@aw5%?)9O8|q+rfRq_`M?M>9lA?`FN|!MSRq45!X#q5b16ncdjfu27EG1n z^gR*LBxu|dxlq?djQYcByTY%3PIa$;bzh+4H;VzeNVrEaJHu!(3ivS(!`Rc7@W%j6 z=c_Rfi<);?03Ed^nSCufmPhGIeiSwDihgG0ZsLs4k8{$Yma& zrR-g6lBq)Jw0Nm1MY$WQMHD1$qZB-X5&45EM*0HeI8*a+q2IJkrY)KpEY8a#obn3u zfK!y-O|jOydKGF6T0chWPGj&=1x<6jW$8xnDiyQ3vtS>ripGk3<7zk6kRl%){q`LW zz2Y<;JN1kXm{#i*r}1lpwPG2%xgi@>UuI1Y8ox&eP`R(Xw79S!PiHcPD@+Z5^+P?n z2sMTz{BBd(X)IAp!@+g^aS&)yDqRsz6Er%DROCg_)Aff+3bfV4GGunr*iYSV(U$~K zw}4MYy10$qIqdE{)_2W*w7Ku45by3QI~Q>$HsBF_x} zXzPs=m3UhrJsyv3E#%yZdK#w_26hA&u|rkF>28D?y{d>;Np}2za7+9g(V&Obx@hL> zS^#RGtc=)SR#ArHjwm5Wqy|i6p(4W=LC{GfFAj~35rznPJYqx-Y|8U7V?ehlkB+7g zxo&6#M@kMe?jqzdB)io3Fd8?V)!nx!)oFN99A+cp!50ljgzsP*BYcO6!4w)#Z8#4k z!u#YrpyjJJTvI8Zj`b}x=p(L$>RV_u&6VaER8^e|qUzu5T!7v&Do)45a4t*;7~X|Jcg?wo&^}A* zjx30dvQ~~Hb(0Nh&V?omYKUCeqdIJ2r0yi z)6y_VO)wWmEK-A-hCwRqQ9}-_;g3-SQO4#z8KjBf#|;FA*g!3q;5S*M2K>Kq#M%ElMk$C9 zW0dN|urNwZ+>WJV(kN9G_TOuiCf=oSMyUzqnPxjJg00yo z6*j4+b4sOSV(?XUW}MD38>L;EoU!(GVpQTRP7^A+T@`HrL`*7dQ`npfVRMc#Y|e`; zu<5?hnT#Eo$c*e{<64=K7Wm3bG(*f_Uj%2(j8yGNI3zaHVUTOg;j{*wq?wQ z{ zYgT{2L+@|Ao~5qZ#Y$}#_ZffgNmC~rS=zsYzjW!lXKvfrbH~D|*Yp|C=lwe_+O*(( zk}JT<3k9rwDIYZ3M{#Glnkf=;r73Jl-Y1G>%#l2S#R6qQpl^LuJ;u`(*Zblj&5J{* zq6RtG#$<}>mc(6?MfC2~a9e$X^o}!g8yxsRDn*Lkpy}$xZwYm+$OT}$t zR|l}VMqQ1FSUgptmInjIO0+enYPk3ZFu@ENRo>hzRW@+b9JdE7brfrM;=&-;QCWlC z(OO-T3^BuJ|2uQ?`Ruz-9COcQZ1xFVQgrvmgVIhq>7sk?89aPAO)%E?fk|%cHeGH= zWUw2g_{~6rK*8PwJ{mO{^+M#M@|H7LOoPBZF9^P?*$e z0j7-9)-?32X{v2fMY-F@E#1TEJ&uwgLHGyFI_;7Q>|t|m)MCtuQ0;~$X_ko&WUH22 z;HHKWi9ljz6b+P7qlo{Xkm1YjlHt!sB4mKAE9AkGA6HkTfYcz7__VuF0sYuFH;=H;bUmTto<`Tlu0~R?FJxLsidHqz?w$J3K*zh{b=1Vs4i&(?qhaP(5^g|9gqDzV>dGEiU z$rrP;GMF@C){N1k+R?-cazXn8{wwK;HtAv04k3>ob8H<(f@i!ysGEfvA*ykA+Ei+K zr9x?#T|E#KW9uAZE%ixMFCt9DrbK$2AviSi`Zm|r4D8?B*0(L9c0Ap;^37_;>^LSR zhWg$cmHZxb?KO5I{DaPXnI2><3%j{a^JSdfR<6ll)1|=Bo?+72fOUrMcUAkc6r|fw z=7mUngpIG|IxLXM@2Vib%O~8bTqnw3(Rl*?NjX*E_$va{HCa{t)XqE=4(BbsS;?*k zS<3Z{ZTx_TKUjSY%Wh@7qN{hog1P z)P*~GZrpa}yO*-yjQ*uZPMCVqxs(L{-v6|M;&MQ%NeS~VpYi_mov z)NO~^!q^-}b6V@9x`036?-d}cwi(&%W`|ka=caE3u zd~YScf;k?(^y;UXZQVIbo_OH0-PHv1-W`rJ0CN+1b$E&aa}fi?FjXn(OdYfa8jEI` zQise$(g)2t*6Jbffi`1J+)2!rX)a!4@AA@uyh_}X2a!28;P`F#%{s4R+TamK&piF^Gq?OQr1rOhA;-;{xM1e-S08im zF_Y&Wc>a0!0Z3rcXYaSlBhC}0Oeqk=X#f_$Tf}xi-bqxd3Fc8NNHRgfjOyLOA9On4 zu(Lj_e*Tz1Yes8f$LP~G{nL5k?t{-8>rwtd0}FY|Qo!}7F=n2m1X=D^OtYm&gvb~&d{S~ z2$V~AvJ?4L1V{KKb2k-T|bceUYhV17H{)fa`p_dbdQC z0mll!Dx?apH!sk)Sq!Y!A=?jGf?}{?i%6pyg5e`ei9J35hneL%ptZIz=kP0vi&e zyafS}FVjt9OZ}(=@_VUJ1v{Ez3l1vH7i2@{1PY;u4ZtqPCLhFKnLc~%mXF`R`JJYz z$KHAPEu)Em9b<+}9d-CHC%C&ayxhO#rrW-{X#OD!o|tjNASGw#o|PklM~vCOA7x1q z{M%hm0RC2KsC0WUrPwXAv>vG0*3;-3C6DYO1niPxhq)o8F?-rPmrcgQ?dd6Q+(6A6 zp9+i|NaJ-l7SQCs!JRzQ0f|8pEXO=)%|Ux(#(vEw4Vtr>4SoM1=DRnO zf1H2+mCydC@?utU$6Ay_{`}y@JElzCeCi873we}(J?_Rc9~(Y#{s!7Z%Z@vwq3(t7 zckJj5=YD(9K*Kvrs{i$#u6M)LJZV_QmSx3*^52y)ib>P$cPt8Pe?**DRUwluu}etZ{@GB zL8hI4HUQ>Vu@iSHKhVR`W>wTy%V>SG?I|gZxfjRR2pAkenF(2 z{L>fs)jF`G8QB9uF2mO=-$E|)Q8l$Tn1WmiR$C;yMS5o@jvKW@8Yt*=vIVNb^k@px z>6F##DH;Vm6IiH|peaJqTj*DMN@C7M-FPh6GwKQITjzEiZT%q5Gh|DnXp-_R3L>gH zr88CSiwT!lscse>6-lJYce|p3A+)!@wPm?p1ti7P9Zf%B&>GI}S|m9!#|=Ki92FUI zDWXrCf&xh@C@v^2D(ogI{k(p~3jF(7>Zn98zaYEAsQL%2$XBHEmSEjKI8ukzI1OXr z8xO|2W&j1(@vaF;gGv{`R-p2))wD$Gqw=q7E=et;F=OO5+5YtY#q!=@)%|ESSAAf55JN^126yVhiX#8cema?Yfr zremE@@>S}_AWU6OZAZjDLKYs95dUUev%0|bj~~sr@_`Cps6;$U zUZ|+gzlG(+*3(6oHD7iqBKL@RvffrD;Pg}e9Kl%#%F;1RElzuZ+BW-%l1p~Vu2{{b zTZrRR+_J_UO>K2=IVxWSvi^edveM$Bh)AY(s=4%VxfRaO8By^>nE$cf6bx7P{!F&8 zdz#o!eWXKz2kM+c<527O!wq-C zi1}F|#z)7F!AOK4+pA9()c>S2c>Uh*?Q2mZkRy%298Mc92GtZmIIAMr4ZhM$%IAU& zP!!D7eu#fkBv(Dwu&JKJOl>b*CS2Iih)lIs0Hfd46S$4pma(4v!_1a%R&C?|Vf7DQ zddNNV<$o&Y@0OTn;T^1sl`3zB4!-4?L+;1Hj=EWb39}?~BtN>yy=cHiQI&F$k8F3K zJ&40`q8JMb%fJk!+FT@R-3iv~DDkfp?@?2nF`?HAXUwXITM&ys&e{CRioDz&H5I)o zdzBRB`t$sPBq<3=l8DDvqzYde%VTTe*?!kKtN#gd;w)3?*#>&{luk9MN!W`ZQfwbb zD=O4c;ctS4vD64=*7v9mWK~r7G#bE6wiF8%>EmyjonT3U^Q~q}CZNKL@iQfvv~lnE z&gqT=kRf}}lSNKk)gct4N;--_5z3?ai!p+PTtL$&aag*ufnGKyG{W(1hL>$2_cgFW z+hoVN+t{k_V&$K)o>M1gI}X_Pi+9hm*llp0a{kwzF^gz_hSxa;V1MRGeS*Dm(%}cQ zaTLk`tc)ogR6jHaWgg<%4b0Ay@&aC8CAwo2QSG~G0k;tKTeJe6N@RN0fB5L5AF_J> zVd{W4&pPYPfvMqju7RO{KCtz@`)E^cKl$|2PY(S@$d0u4d&e&E{eI~<$d1R*5M?PY zhhl@nC3$Sp9HhvS4~~N(qAn_}RtP$JJcjNd3$?5Sni8OsF;AK#H3h?V$%fZ7iMNG3 z4)qu1O_E*T#vEupZBTVDXvON~+o5RQ2~QUqM%lJ0v{R?-_DynScpLrZ%ddG}Q#Hok zxDk7!K$;n~H|Y8T_qvRF_F8QTfBffhGLn&-n_G}uK$7BBLnHzV!3vXpM1bFCCH8!< zn0lE&&b7fG`u_fZj04@lTjJ0n?%KPD!+h|8a-%$X)D#uRk?!_mtT^I66zM_7P%Cbr zN;hsn96K_>j?&zctQ^4)Hl%6-)5jt<^e6^4-OtGUN z#nD#BQxNhM`*|Ef-IOny_+*94rJ=FbNI2~3%u5}97vQgyW=HLha%xP4#`g1Q>szA` zuv`*{MQTNsc2l=RjZR#=ej!1x1nlblMvoYKU1Frz8md}X7 zEI@dn#q&J)66&dEX&E5FEy1!l4ABT9FITk$%Sv(sc>(QYOk^-I^eI7T8A;j5UC4H5 zJ^7KQpPg%kI)C`6+uPf3KkBI4rf|t49u^^5Pd4+|!xrHT#CB;P% zy(r=lmQ06Ugl0^jSALEf4VnPm)ie&2ehWJ&{IGqSuyuP#*J_79DVbsSOcYF z8&?8rcWhBG&-96G`eds5U`ebJQWIK01hu=fU;8|Y1W{|y&?+xCP?6gsuZMO#B$5Us zp3=NZg!0vREB(z%435KSPUpW?KXSx{`ZP{!s_c2(69#fNBPIEnxIUN`Ar z2l^tIbTFx39ElKn>l3lUVnG!C3U0E54UCK& z(!*6!qx7w@5<>Q!IeSkve)E37K2itH`nXIkzpIupzp&1Of_isU#e;wCOy)82){!(!5+ z&I8p>As+B39y9=Ny9=Eo5Y@LIH@=&U=;WzRxY5)2E)zG75#JlTugUN2)8+ENk1w#J z+{rkHT(z(G(cSYA9l~nl0o_B?|HKWSx<}kUVvm&KYPVIogzT_r9Z;95>i`#xFGE0E z>=f(czoCo8u|x~z=jKw53^k%N*=cDB?KEbU4q_eyXC=ttFO3us`M4t|9eCuC2TnRN zeAVIj+w@5jrdi9h^S>W{n90v$=VXLW8{W||JUD&&@S!uNN9Mc<{o?-4&@bm1vQ2(A zsSCQdv^(u+wv4!C2b!Cq7}HAFdUd`wj%azgxqTYyY6BIe#gTn4B4w8QJ~o-CD{}Gi zER*l#m&kLbi?MhIsUu0ir6W?@ zV=Wgpv7JPguzr4?0~x54Rv7$Ot9x9s16}nTC^>gJMUHoZ!-b?J;Et?D%mstfEDws> zgv(-O1O=Axd@6G*E3&GDt>GaD+fqEDD|@Vn68g@(q^XgSwJr%GeBh}b#p;wmK8 zSg%R6DxPNlgJ9mL-4EvN3B?8Tu+PHlHCmNQCk8!aqf)#;j}`bO#KWL(#eJ$7^YJ26 zL=0c4LQiEoTCC}7qVP7>b>CX;EMkCm9QKWwdsbYxHXrZ*VU zK>Y2S#!^!pDX9)955IyGr1F6%}N9 z&r(+-m>2b7JcZyHn{H8D-MU4wI;;3A-PFYq{}=PWa^NAJ5cFW4XH*QbB6jdsEFtI| zs!p&d7RO+!HyeR^iVJrV^okh*W`*LrF@sF4`E2_jYJjP%-iM$*i#mqPkGT;+EY*o% zD-&&iO;;f1TQ(qSy(kXR%(61-`H`z$mx!Qir`IJkz_ha~lh6dUvcJ=tlh4({t^cp_ zL};s1JP#GY|ITlSqVa_6mSX0~dBM~MOt@69x%5-94hg=9-l4spJ}eU>d0|{4#VY>h ztD%dEe>BNWO$ zMoLLhU9%G~Gb5fXO$J#KKM+TntiDYceOFgep^=AD)w&>$75~qHjX?xe^S<)!5eO-}LjmAw=lNicgrUEel3Xu=?-^1}Fp%a{P{Bc$@JJb+H{MQNZP5!43p1n) zug~XoppJuhMI6()Cg<|O$ieTGfrRrG)-wZHs4aX5-^^djjn0e?P zH3vp~CvspiL?-vuPwHQ*Rw0(8I6W9Yp=ZHpu(&l?unCK?U85GAFg|usun4 z5R90oVu|LS(fpv@SgHLct~kQ%-~4s{b_Rb7f4q}{#O2r7%dC<0&tv_?rkV?$%-VAc z;JM#+DE5}h51NmhPkYM?+fZc*>c1(pNA3S`HLZ~m`Q$<>GC{!1d|0zNzAT-1RCyI6 zA8uoEai`Rhy@n?lN2jNvd`KPM8}X$+>i(*W4_FQ`5r)zLg(sx6i_XlDVF9f#)0gGd zVWq~(F|aZp?3PZ!+^pIyF;Z>ny`<9ee*qhLnVrJ+n${I6ddJ1Vb`S_qBu$Umb%oTs z+R=fgcqi(+Nhe)VFkhlcuBNS|e}o)t+DFkl$bzs@N~%*N*a$;q#o!U$Z|0!zkDA^p zl8!XtLT7oQCIBjw!iAej=!xh^p1{RnKRRj=wGR8+Q5l*5l}PUu>}2>LLS}GZpMkTj zjxwBeiPkUSQ^;MX47FHQR$fNrSFH#PW-9fT>e6kY#=wMxH}75WB>oCMZj;`Z=<=PR zAF*V=7iUb0R2{7JU?x3ko7gxTva4{&XbG4gM2eJ>m6b&;{M<6`iPZnG$JxPbG8@hP z>!jjw;UaNqL^q;JL7wT41kaKlp&~1F1dbSjpT7f#E zz8{z3qUj}af9FMig4UjCspx6yP0dTo(}1MDcP1dKOo^~2#!@2wL?j!f6ZxqS^5Yj3o0|M!ypsvaM9^5v555aCJ}z{m zp|5yTuy?7KI>@W0E9)Rh9YSp$Z8I@}&1UOoqm#HyD)U$6_^X^QWF@*;gN)E2rMBeZ zEOE0^c@TzfVBgg!>`r+AzrVK6uwf%do^{)4ZJgc8TEgqtp%qP|gZRdGfBDdWI z15&lb=@=nkoGLJy-4?Zk>^Ad~U;#{c@eWWOs?PKo*=%K{zAPqHRhITB!_?iZ5?_gq zpT`nEa~(&NfF{Cw|5yRFAcVV7H`eq&4SqN~oX0?eqsExpP~{+nRih6o6ESuGC7$!Z zqj}@0vQw}JNMwRRrDz}@hX4)K5@U3oNWaA(gLq~gUvtW;v&LW2cSQBXs)-Tq~kx7DOW}2Zh-_T_~J!G^OE#WJSf(ad$|}61Ct~TS*iaoyV@dXjj!0o{NeH9-RA{UPaGoG#Uh2F(Na&Gi zaatsw++k6tsjCo;#i-E`{&7S(Xdg@s!UbVcv`(;QzuA*Z2)-UZu|UDy%ZQn1`Ve-T zS1QMC7Aol?QmVB{$1N6`L?c2Gt<5U@1aED%zh_0yqJpe)U%4(7)|$+h*ll9!bb?qh_9WdW*r4?d-xeI-)=U`vXa)ea*E1CUf%MpQ+b=}_!2 z3ZWXhD;svR6_@CYkk8c2I130}hHPjuj+nIB}M4|AHl&|t`*DhTS81GTE*ckuyO5ersgg}Kbo9byOa~c^? zxY4GcQur;+WJzJ-EyA#W#gfwXp9{1tla7jmrU+i2jX7-SV4x`FLOWLoD@wo?^C?+Z zCP;G_hQ86B%WIk+Oxlrbso{{>RB^A4zGN&oW+r&BSq?4 zAZ7lDerU$X;$XMNXkQPcSiAPJD99<;7-h89#BgNnY7(J1%hklPsL!Gq*11({iRb?{ zt5&DWB*-ac7SD~Mi$EF@>UOx$Pe-u{C1yt2{8=>8!IIou;wokIVdz$r(MA)c-z`x<2n6(7ic4WR+J2DsyP72SWQuOf=$E%~Q0m7v(dC?{ozMsfRVf)Jk>=QO3Jh<1XYnLn2LSu_r z=7LFM9Bj`F+wxtrj(+f1PqA7rqev<|6Y%b0oC@e`5rIg=69AO3b2Ol6JO+R&EkVpx z3RINTme%IydW*A)HQ-V#fHT`?Q9heBz|&$qwm9hBX}X`L|4IIZj>{xL+k6u)Fo{Ka z9=eVRk&ebCCx=L!lb@5Hmm5WBD_jhzwT7Lojgr|o$X%~SBsOw>6-vE=b@_R|j8r6i zxiE@r92`qD5Mk8i1;!4%Aa8maET2MFD7?z@8V3j&bztRTw@{hIBm9rcFXw;gj+%Mk z`iANO_blAM==kuO4I83^0pA~e{lJRfQgaZ4xEFiqOcB>ZpB3zJoR>5t0rX|<_y;^l z^!G)B6a5ki0h$VLg_zONf>K8mkWwM!y-G0jk?qFNXUgDBHb21naL@PYJOmpg^8O6+ zwM^eYL-NpI6oH|rucB>$Sbr+wSE(7P8R=;huESS~F%_n-q&iE}*X~)PTxb4j`6ilr z$E20~LHn)1FH4#p^q}vK3iuz5C_s1&oMS-3%Cu=q3bHR_(cP;5`(3Kvx5FT}G&|9Ty>h4pQF^5K-L@ zn5Z=nIyK?@s{-X}nQv36LT$_-7>HLXIy7=!9WIPFNPv@a&WO@iBPX~M~5lBTd%L=jA&giLoZLZ-F&H1e<_ zaHvO*-xQ#>2B#<(HH^vv;JARb)ebQEI%e1}*Swo3=p*rIosRQC$82#^0gmc8DC1&g zz?oswX26N9nVpJjxQujdbBcKzCPo{@Q6{)TFB&ybX2_yOY*@f%gR=r@hgL!ej=H@O zfQTH$P&NtJ^gbLW*!bZ_K*kK4!+dKA*pLsP>dHLn_-MYf3(R0>3 zmCH<8O-}y`+b^_WyZM$1@bg9u{tW8yuG3Xk%@G-#GM1(G8QU$tcWo4$N zdfe#7WJ7Iw0gV~%!IYYQy3TE;cq`STrpqWyRlB}9Y&Y;X-hO*NlwIG@YtX`PJhteq z4X4by>!{!*pYa__Zn^G&hDn$5k1oDh*U69ke$HVx95v?7D-UPNKlX2Gyb44C{9BP) z)gKug>9~_ZmIbt<9R}PSxc?YiD$^l`(PiS~3i!R%exdt(I0~9SO8k|H@dVYC z{;kl7$L(N)?zn@$vV$*0umu0!xOwwNzSJf0xhwe2l`Hwq<$R3%^piK;{33Bh+KzMj z6MVm28Z>rw6Qm6`3_8k@;m|~ zxPmJ@d`(_G(n`p1svIS6XS3z)yZ@(x2$3Vm}YhDAH|vcR1fcZ%oWql`5&n z8*<_idxc3%-_!w&28zSD1FM^*3FYPGmF1P$)z#INbVZ>-?otX|E(S+RoUtzOOYR=geBYLjp+ zSsM?kdm{=>1-21>YDN%r_DT9e?h^sA=&%Yk|h0&d;P& zOx4anuSVuUS_uRL`v6{8J%kjKVbQ=5U(tjc6V0>&H{p(vWM$*~v0R=L^%?pZMt%%( zAEtk*NE6|r5rnA{WhZM8wz!3X!E%`6(A~$&=Z4R*PY)d-_Y7}kS^UR0l_I759sXmu zXF<3T)P%u>PS8|mTMW57Mg_!=>ys2WRX{oBA+4B1+sRN}Z?_AE~?nT03)wcQ|JK3hPvqq0X4+gZZ_%p-4LUX0@ke%69{#^ItHp62YkrPD_5pw6Bfk0t z*7(zU*7VX3E-764BftOU>~Kg{d~4a{o$}}5(r@{%#kODfq!u#UuK*3OVHT?MUFd-t z>A)wda}oa~2cCo@pPDAAdVoY$X~%u7qBPT3#~l2C{v|21j72t(KOJwZk!ota7z0gO z0(+*V+Rzc+Oc@6iU~XgPIqR5md?UN%g-?HEx8rIlgRS`SlNZ@7;ncG>o_QPp=84Do z*SDRy@hs-aT*#KOJmzQ5heCWf|CE2jkAeIDJDb$^Jy!ql1~&Zp=lSy+9_Cx$>&sUI z5?LAtSv$(L6fpIY&JJeuMAOe6%xNPu(dkr06KU$qu^81E@HCv#A_E_>Pn8)i1L?}> zg&+ZaCMic5j}QrRYilK`wpT3@dJR%|Knmd9EmE)+A3Qo%+ciZ(eQsn!`Af+X#<8Y^ zJz3262YvRw4*J+!O8{uSbgbaMQ|CTvq(Bp-@Z*Gy7J~>yP^O`+F^CZ@4=OS0YwPQJ zL=mIAa)DB3<+W~5BI7{T$e5OknJ zS0h(@47o~>AQwli$RbKT)2KBodZi#%CfbTe#L1Nmp<0@AkW+#gb?nrxDYa+o@2q*y zi6<^jmRQb9PCMc7%LmLH{4%sA?J>`Z8m;VTYf`2=Vf1`s4 zEyAQx8@j2x9XJkEvq5P$HlHeZWlkKL7PK^$1&bqFGj^?@rr4tWT5B6?8x7%0N?&1e z4mJ6O^dX_4#jo`324yqYk~5F*H&70p%h#|#vh;O1PCR|xutWB{X69TzBAY!{44G8+ zt_Phn4LYw-x2~YGCY}bJjempA#{O2j)+Zn{dfRkPW!m5BX#k5dYN;S{mL&CSH3@A} zyBk8AEs|L5g23`ST@!dW`=fcti6>v66rG0yq9$1aM+Eq_eD*kz7xaA-{ja~%bY!cv zStVVjkYlvvg~mk7a0Q}FKXUXk{Yay7AO$~z{jDCc?M{zLtY^5R^=YlK*6Fd5@5uUK zm9Z2opw~df#y%u?P~6x^TDGyZu@&Pk%(|A0!)_nXs;=#aPz#;Y@&kW5J6pkoA0n7wc4BWDqazG;bT-HhS>D{;iEDL^GI@mJCz!sP#YM?`T05$w$CA z4DE1`16^||Jm8Y^jy_=eAw%j1UvkdTZ5=ZP_H0|&KKax>Rmt+qeL&r;Ya1I!HC4^J zxv6O+-#B~n{VfxlP8m8|)nzy<+@~7+dncNIBS*yXuU8xcy1~9axIsvgd%Y4pl8AZv zk`&x8j&VsTG)u-YHf;Uh2HBW zy`*Wq9w2hhh*MV-%)n0^rk`k9uL7#0Z%%lbi14PLu#SiMDo**$uzC>V z540}=foXwLQs%}!ZvHCxQlUL6qGgu z)7pl_@u#OwHo+eZ$T2)ID=Mo}-^X~yf;;V2&*4~$^rbY31P~795bWEBuYf`~&%U1*k#66A0^ksBh#=S`J zUHf|Qy*e0FPt4%R;K<>_EYHo%WVUsT1SIb;n|1Vlf5mr>=D&K1njLA_JGQ-Bu&-5L zT9tt;RR(+{1N(ugG)^uwLFl};6?y0Bmz__Plw`(W-?;rQDng20b zBZmDI!60pXmHC4Q#huI7Z%hx~*=tFyqB3zpLP7Z}2Zg1eL=1@T1R~6WvN>K*_BAHasi;H(6tZ+< zc#3wH-YQ)fOcB@Vy5CGLaJ?ZB(Km(dY8+Ly{?KVkc+E8IS@p8P8CqbmjkUGt!AOjW z+qV=}?e<-3^ts)gEBT0*o2N=mr(WoGrGLi;fP5UW9E5UHqzx-Q94RFBxco_y4TRijuns~vpfB{Qb=b@$3XypA6#|Drr}q#ffiYfir}JVt)> zs$~HlvfGag9TZ05w7mB#+~KymCgc0cF;B$=xoDG0kQbxjJxZvwaSm$f{y-E_X($xq zZw@49TPz}6azrXlc?l-8={UK&!inztZHIM^*Y4?b+20vT= zlnG0NKV`sCG&w@!n|UUFjCbpZmf>%bZApT4glkD%k5WX>`ZVYQN)|r&hMz8rsBfUvNWfpTTVnt=Bk4`%2SexqidYvebfd-^s z<*4dG9w?GP)iLS*?Y3~JKij|#W&c>o*X-b}Y{Lalw!gEEw^C+icng1q|L`RL`c`I} zb5HxF>#)}Why646yGYQh3Rd9XY%#Qw1_C9?iEr1__afiy-LAaJ8bWw2dpBIZ3&lu? zvpy7FspXTpkr#GR(BorHmW4YcQXEtmMiUWHioh003Ix!O>M%Sfx4V#V3WOQ+i@tg@ z5pb@j8OA;0ms9$VDD9NzE;ZUiRf|4T#5_;7HRgY6oP({Q!M9W$3cE#3 zTIFvj*YS<4_LuDq6MJUo9#(MRJUx$f&zjAf!xQ*5U-BPZsqm9L=bQ>ojpAPCZ{S^q zlq=0rF{?sDK#`PKKHfpeZ?<;u&#(SnOK}KLnpvqDQ$N^pIWBdBxK~FKJgT(NSk3Fr z@w%Nf)T|l#W#~|V;4Sf;a)1>N8+GBx(+|z)WBciA{{HS2uPYCP4=ndB;9mK+fm_+} ztVHS9UZGDt2^fTh9G=Y0l(ga)u(wR?afNAtq;Y$I0 zsTzD~EG0#rp+ZUV!+;0$t)j8l&})QDqQxSsGB;P!3Jocv(!ee~A$AOe?;1l#1Ajj; zmVf>hE|zx)9z2U0zF9b9dr1?5W2K>*Yq3q`e|gUE^2fyKgm0h!E|sX%&aAzl~u! zw=LI+{1#Xe&Rp51T(OzI@T`*!bbo#KIjTdw<$oo}^2%lba|3M;>v zu{A3;ZfEQ{{#5Z5;d?Gx{`9psy&QUM=@k#Ixcr{b|SFvP@`iOgDnAvCYxci`_2N z8r7l-wT6P3$QrIiR?voO!!NA z1iy*b&i?!csFS`o*thY!l(R#=JwGVZaY-KxVb6umBbfPk$1i|6SLz#Vq!J_01`>;G zb;|vxK94YuaRg9;g5QDNij}zmf4DH6g@)c3x16Pzc76q zb+1&3peUL$!)I~_P9|Lwc%@9QH{0)U76OcZnpRTPk)v|d;DwhCOR2PH@Xg_os7v`nhVy8PEYmboPi_UxRuLl6QaranH;1X6BXT zp?yHMFEiJhTV3fQ)gGI(-z53`$V``|$_h!b&ejhd{R=y|bkD$2c91f;l&|{bzx=^c zt8D!}Pq6W92!EMBs+@`ZjPd+Yj`TOdzf5O3Xq6&q=UTqXF9~VyGek-;4lrMgoTJ;NOu*XBekBg*gtCCUl=8bE7fhb3By^iG_U%r1{38kqLQGpLd|;jRlpRI&{K;(ms&EVO6A`b ztatgh?AXnl`L*Ac^EWGy{l0k%JMNpENU%=*5uf^hq9TW%oY^$(WfQ;Zw_o_xoA@8h z{W?4Tm*3ceH~4S-LmmqAPi$lMJjp8eg2vT?|HaPTg8v7PT}>nH%M6uN?)u2d+#4RJsB~}cWLRv-r<=kDwH|}o5{`r{oVBT;F+o6iXE-K48ix<1E z0~{IB3>A(5^%a6W<-~q1A5TuEeFjbszxbJs?!Q9X(S%A+c^`MC!OoVG>(sh_bOC{kF z*||a)SE4U`rX8DrOf*b74PcNxemQKF<&qCMLyaH3wg?62tckyI!d_`9U(FXXlmkqA zdqriL^6sCKQc)Iwzjzn`mzkS;T~t@)gKd3O&=dJ))s`qD0B|;#01&EH6vPJX;8wA| z0vZ^4**?n*>eOIHc6N3iehHvSBU=Iu@Dl*;6h=Zk@Oy61%mWkc@}Itu{t(HzB4@Sse3Zn%^)vGvkQDhjk(I# zG;Cs+Bn+UT)f^;i9ym&!JS534O)&Th>Fo<6L{xjA#3RsL4*ksdX7H8oT)^L8O(!sE zDVxL=Gx^SUmRFXA;KhYLDGOi+-i!C11>gQdd}khYCqpAG_!V&5NR||tDd>p;e@YA% zEk{+5P74~Zi+KZ;;@D)sT>W1B#RQ9ES@?M4yCVRF?}tq94)0JQ2v5_Ya_ojoFe!w5 ztM>p;ic}UX#`!2glp`C?j+P7fFVg|xAZuO!6=F^Do%nq_Yy6{1+sQ(?3%Z7QiRW&- zGebID-Om-mx-G|S7BpegHH-QWX$BqqC^coLpqdNdR}_2HvPPZxaE<&eX zcOk{zticQ2vrZWp0Tp1knV@7;YwFkr|E)qA6zq>Ug@SO#B078!EnNiJgeNW24V9w< z(F}NQj0qpE);Xc368#-~6`3w)DRhHy)Ux^kPO@1YPJe3_`Dj_*9GJFb-7IGV3qSbs zj`vQ4FE9U>@!$XWY)d|WCHrwU=4Z}M-ta14`o|x9^;7aC8{WN}l|2rxrhO3fqFL#cL^{%6?rbeWgT`G>`6gr zse>Zc(Bxv?kG2LUDt$^b%do|O9aadgbO7U83S$&f(P>p>Nl}h3EyYxJ>T%`?yN!V} z_E_URrr;L6hhLX}2`_dZ+`Xb(bt8u6acGTs-6*~lvbG$1^>WBsCNv1*aKOyY#!=lc zg%F5=qITRk&mdd{-u#Lh*rsH%lg_9>{EbzM+RJ7?{hKBhH#;up8~8Mj8=?3WY~AzE z-pmI5Ab*6PowZ}b`p*?-XqPf%6zlcI^UQx|$SZ&O`h%<1(B9q74{}@yc_n%_f*}%n9Wt+-C}VIr1F2WGlP1pqD!m^dj*WF~S+y9!+S6wyj?Yf(fGjsjvo{lcFy@HswI%YyA;D+u*c z;XNl?!x-6@Rn&J8kzIHXwnZqt!azX>MO5tQ2{4EJ+YW#W$p!r)5tRymDcT(8<@&tn zvFDPiSd|u0k-aUb3a5RU=Wmd)Ph}^-&&`6Riny>Jsz}-JGrwljTkIddf5;}imd0Ok zo%8Bx%f7zQDgPW!x1aaf)eARY;9-MOH}boSe`5EtynA7;zPRUZ{`!08Jv;jzzV+Yy z*XQ`>D`q`?&RcxTor2z5_I~C%z*UAA(IDw;HSIp7XEDADTM}Ikih{6Gw!|fd;V*4pN7Ug z>_N0W?fGA}_zV8kbv$$@fAdBD=IWHi?4)gM(z|T(YwP)~|KSg8-|Vz|xOd4(eU2G#D* z!A^va<0JnD9S2ZGYxlQgf?$&Op5I?%FR>Xv{lum`wa3r;vfN{ta?-FV{OuL|2fkqI zTi?q+DXV^ae;W_mxBj{x^Rg>H{DzO?Z$37S|HNN!>CMCahi_pQu+-zrnSV+jkg$+A<9 z=kRa7x;Qj>WMIXAfQX$!rhzGx&NgZ@42O0}M1-0jZRsbr3UHZ?bK7l|#941(XZA^3v#{@Y)el^>d zjeh0P1FI$utqi}wZwi;O3G%=^$DH>*;or<`-}M|!cX&&H;jF#ix|V=eg%Uan!Q)0d z0|k0R9amJ21;Py{emkMJold}jE)zm`)KYr_>PQm5gyIQe9l*yj)yF853ehvl3zt}r z7PK+Ac(}-@Qna=yROb(mepUF~X)jD+2eNDVHT++;^`Cyb@xkZ##_)M`{)BHYDCW;$ zK*=}!ZqVq(m8;)q47EFcXSiS@`TflPlWQStu14}fe2`r>4m!J}5K5c5*#Ur8c!T5t z3R*6ZCIl+$dsH?C8mnvU7>>j2x$1ccv88SabPL^FE!WsLJefurG{=VB;-a~i1)0*A;p^Ny1t*m6oh2suB=FORGI^X}*HxGI2 zswIz3J@gUhvh&u2ck-wHc7kK66lDTaku;#?S)4>$RHl2( z`NWQ6VaAVLo!7~-1(9VmRI|&sC0wDuXev^z+=0rR>MFf6jp&x6HK(E8_6U%piwYeB z*;#Z_u4nbW!uETXJk0_t@8;W{Ie+EMA|Kys8@XxohF4yF?cyh=A*NH&>!d@Tw@JvBgCgN zwR*!7^nKc^h&(cDxK*Ni#L$PlP>*nBl;uBOQxXeIYk zJZtzTx8KKlyzn}cM=1yHedy)A(p38x{%eS344QTAypvaT9C`Qr+1DR4JhNQh#O8kU zHJi2mlSkNDH2s48KC+gT_$MPOZp^^dVbLW z0e7}!`x>(i1R1GsQiP0EmJ1Df(k)elo;@Oq6A@N_0Jpx>g28|Q`ZA8f+FGJ;QC%IA z>U!1n>RC_jMugIy?o*m_G?Jk<&!K#cZqi(NM(fF-(|)y}eUr2`nBKcVM*9v`d})`H z_)_Bqbnl3xzB7vY74fSK(mSI_kHD*VUn08et0_Wje6bbLwS`3V0zveqx~6E7ySq#}0{Elz{TsA+)ed z!fWd^y&fm*Cc2}IbF7%Vx=(XTJXCu{Bt!J@<62EocM0ESR1ZqCc{dzYj zabhppKE%+~LhDpg$A_3@2KjXrqGqYp=t~nZKwlm-RwiOWtiBo0zplBiIa>IW(Kj(= zLdmd$S41_M=q^(pi|;R?^X4I;kD8?c(mUWyW1o25P_Kv>nu9enF&0GE@pP^tgD-ZK z&KXSE(pORzUbVI|0b`8S#=-=A(HD|R%3~$PsT&RnDq(RrLQM;`J0>R3~BD)+`qM@F76~rO+u?hSK}nZH-4MB6sNmv zQxBHtq8pue>RxJ+`b$3t)B81ncQL0sA~3`@be8CvZMBKkCg2)cEt}rmCDWppcY|jo z&CNQ;dN)KXzmz2S6bJV;78qS0GK1RkmTxczd-WMM9zKAo{Qlj=>OSc|0xQc8k zlj%Gl@xzH&tiK@G$+L<0tvxIDXfVj6;F#bT)X(&9Mjwr6o~L!6=Za+^M5JOS86lV# zUj(66%XCvB9P2u(i&5?}F(K^|CWov!j=yQ#4&< z&iL~<+ES`Xu+-plw7FDoUY>}x`XeaFiGjn)my0!M>w4Fru|)L#PQsv7+j%6zF@BV2 z>(Nc_DF4>FdElH4PjURqrP@gSrJ<~MFsp3{Qhqdj4IPd?QL2pueeGuM#kh8Yy4D(d zn8QNIUy~5kiEisbmMrvjq6ZBYc}PjeK1{|fbZ*qci5O<_g1BcBZN~U#NnckE7*G$J zYQWF|Lz^0VH`Mj7?;p+4Bs6wR=K`PJDkLc$e_&L-ADo2ezI&RfO+m^O!;O#S{V-M& z%nI&j&3p8L*eBNaP54~bobMm~Y%+|Gc{Dk`ml4+o4YcNY5*j&%=ld2e;1QW$0q=zTv-+Ml%seWTijD3)8l$&-epmrnM+FwoTP8);2Jd%v}lCk+u0 zMpEq4wD||dKAlJiEZz|Ra3V2?f0(v^mAd`+A3b2$fMM3ckluZhEk+oWzY?;Dh{UJ+ z61<3#i9%S$%jl#Y9jp5zC=%=gu=63R~A|$ z6kR3^J3$)F4h!avYLipkXzQ9{vKA-#0SW04}AvIz&MEaeO;uzfJ z^SY39q(+1!@(vP`p!GWv$coi_61^gU$XL9BM5cb&Fjbih8$E3F$PtMxip;;DP(<@F zz3b>c+7&bRA%GJlIhuW8j^^mHqwABQke#u?tWiTDJ0`i&t;~MdoDduIE7>Qp8PwI| zT}#b=maj-gXsq9moXpe=8%Byom6*06)(23=KFbT+_pp&k*YKZ`jvz&ei+fI7I7zon zw3tDER98-6*90pDqWj87bYB_4(%gp}EVDF|>^Q~^5G`0p?@;FyoJVPhD(Q}jsdGvi zda=Ysm(n9TDAI+6bkP1uy~~iS^oTx+ypQzJxUS!qNX#tW*`3!VlDYWT2|YD&BFWsu zDHErRM+1*x==5SOciw%LJKx_Jx-tpkviOKaQcuG7p^8xm@o1`Drbe5tD2m57gQ5a+zc#ty9S~ zh1HC>noGph`bodU<)Tz0zV2jhCu*IHuC&P+uFaE!d3y2Pt>S(@n~O(&ip^@hHb zQSVP6F2+-F4_5;>tvc8p@muXV*o>G3{eH|seQ z)F+?^vVp&h;}1ff4re=(b(~tOun2QHVlIaf^0Zv4b4KJ~M>vBZW@LE+wiu64$TPA! z0Z;VRf+v4N&X5n&z>?v_q|n&za=@m})z#x&YTUWM)W${zvxW?mJyup|tK{~$7pI}% z(39AAN$A-Q?BZ#IO9|cEHmf%zVw}ZWf|f5x#LM^>5L>kX$bhy1ZG#6{+W~(e03te8 zC&O{mX@5&H!R{yp>Yf|vW39%(w7f?&_Y0a$N zyNkbTKX($T>h6BFh!|;O^eney0zO;4C8_;v@q+*Teip+{_HjRdCB~HPix{})>Z8gG z`{zq>z6ROQxbw9q)mNoBoGBO^=y13?a8-o?mb7oo2d#)m37e)EyRuJL&lnq9M4=4p zslj+oa^c&U%W;hPn=GCp9nfU)R01hcLrwTT%ZCyv0}V)s!_;6IllB{ei5@MKix6v0 zB@@PsO!Mdm@6K5vD!P&ph47g5k6v)lNppq|+@HPj*!iQ6+<#n4zx}%@54NKncdr{h zV#?68dtPKcT6<8>0{yI4Wud<`kv(Xs-S^XVE|Nl|bhjscUK)DgrgTUtDHf8E5s4FI zY8@EIt|lQF-rd(2Y|n^<2qLedki*cpS4k;=iKV!$<;U_xBuXPYc~K&fvUpji?@1(O z@$V4=*MrWS3FFX0b_9hM1`e=3fA&4N!2Yr%nocFY|6dTwsI$uBiIq^bHpPj0rhL@i zj0z4faLNt^{q#_r0!8MqD>Y!w0NFwU)sdx+*`8@4DJbe{oT^FC$IL zXZe)2%+QL8?v-=ZX~t;FMkPWh%JHFRmD3~b=|5*-k5sJ+y`C7%l^3f>Ovf zx2y0zX1^wMLDmtMk6W~I)s>-tT(;@mf6Z9Yf&Is%N#Qm2n^8;al1eZ$7lrYt;~qza zlPKyCIAmpIDi~ku^V1Y!z&Q!kZ6~ogY+(3D{;@;abB*oz-G$-1<-_T|5`}1ccc2&E zbX3~4O6LYsXf&_ii6P@;r+Famn1qKW7B;FGmr!3Tx@L%K9vTd*-bi4yueJma6O>|l zv9YMrS3q5Q`Zm}1=vh@EM%q@3N+&OsHBotT9(qTK5z;7w8iIPx)_ye&443>=6-R^b zKN6 zb;^q`|Ld9O@3?JcKI@hC5dY$_XZQ#AFM0f)(@{TkILfa)?9g2PmhYjR}--+~&Oda1W`U~obL zY;erFZ7%m5=)J^f_3YWRchBBgI(8{8^dL(DJ5kw^B!+w^Rkw73r_vy*T4d=;@NEP5 zmWBFS^qN3vI_l>UbEH~YHF6Cmp;k?=ES)bw8`M_Q==I#}^i(N}WjQf)-EOd`r7u`S zg@cNLC-uBlISPJdKE*$|=iEo`IE5X=GBH^DRDSWAWmm6Sed8Njwr#rf)@!a?R&3%6 zKbc?2|G^KF_YS>aZu^-iXZ4~O(+O4`yU%h5Y&5x>Z<4!||JQQIq;tWG8}&Vu1KSat zkQsGDTZFoI2a4bq0codQLguQ50dUcF(|jBDkgvXi_7LGheFM?wl=g{V>>5(lC?Cm2 z&2ps0Aof%}lc)dt`kU`tdikns{$?H;YPhj)4nO^##}~8v`3KKD#y?+`&U)poTz>oW zj6HSrzvVCPns?ZdXCB}F&JW*j?O4;h_v>?)tUqDFJ4+XwxUhZh`NS7lS_oX8h{#nc zP8z~SbV;QSL*je^^i;%71$R+$1r&9;5~4(V9u1IG=mlng8)b>gr|1sNTOAs{e8m@JicJ#{wl( zF>@4Qzkg=?+zX(y&PToVR^Tv*v9YU5q4YC-S(sh!hc>G!lF{yN95g)@?MM9_BWq~p zqyyUUKeHZ%ipGdo&8n3;;CwhnN1z8Fq)TjCx=-y=MTtu0z9Sbrx@pZ~+&d%#CkZGFRMpE50zGMSd1OnQZoLg)-4O#)&= zQRzYeFGZ|aFo+;FLao|5 z5cc|(bt-2LTYkUZuB^y{tHw-vxR(o7`p25hiED*|tN_Vf)5!%x8J?%`Skp{HiZo z1gdKw&h3T%?kn1&0?NPKCb8Lw7Z$c}S6E(H?pyJQX0A93&9rJR6+A&XRu`a!k{g;% z<)_JNl@HpZ2YjElr6qMVY5o>y-_FksroX`rfun+(tFCz-4oWV%05#-Se4OFUBMRh? zGNGL?+&!diE74>qX0nMSf>a4Z94tZuO}$#GF#@geAeYf)rZL5?yu7fmyj^*_HWh*5 zA+njw;w_`~XVEm1SEdK1&s=~uUSScKDMNL&+WsM4A^Hq(_itMU`e@QJq8@F~v#}|p z(FkMrZ6R}WnI(jEOP(y1pRT$Ip1V=W@< z1!Yrh5m>_drn_Q9gA-P?okgK5gd>%uXk#eRMyYjnORbMq>k8(YEN|yYqo2C$jvE)g zvHgkrUg*>X6EgkQ?QQr=qanZd+xxM3E z?O5cxl`PUTi#<=eB%{6>$}Z?(Hevi5XcVb-iXh*W!|&mQk+0@$N>YW|M}r(v2>@H| z0ijylFg63Oz#nC~?sK)_Ev{8>A248ThBeb-Q81~590e{ry0@b#EqeN?Z6|f?55AwJ zTFbv*d4i?#{i~jy_n|baW`a&Qch?hWn*QzCpOup@Y{o!e(t}mlVN2TJd6^vqeJZvR z9JQEb(I^@j>SZEb)(AcUS2co|JKAoy$Jyi1Qxgap4Cs6s4ncIPm%+feK8itGWrdnrXO3#5Il3z;{Vw z1c(9wlI_VV{`v+N*jFtPh6rMrs+fhLf;_5D8}qF@(IG{c=cPkZVjvyxTL>Kz(X#FW zWB}7#fC}C#8Y6;HTYp_c>kt2(x6wct0?hR(X<?z(&(a^3B(d)vH2A+xOcaE)S zC)P|szxd2>LO~uCMqkrp#A=mSGKEDxIwd6r(FwnX(CL40-1C2J(=?S7A}1z;m`CgjS-c*~KJVh3ZTWVq7&V#jm=vEx$)!%&dj8>H}s zp`cd?1!{S|eMtP3jLuZgyCc$*K__hoY6MM(bOo8vMC?Lj%JyG*0V)Mw*cg#wQc{RS z4u`q{jSX>_HCQVQL!LbWdS4jYh_}~M_Pn$~?yn6U7R!WMjU0&%X3)EEG^a76Qrr?H zYD6rh5`b#n4FXlZ_jXKc%?-HdXf}8v{ADT(I_koi7o#6VllZP zt@>{*MFUSw2Jw6FT`xa<6A3(zZsfmQQInbIhi0BQf z4k1KuRDrLf`s7Ab`5?Ln74ynLJYR%->_0Th$F$1Jm4ew`{U{9Wi^&YlSnbG3i9i%4L%C9HZ2h(`5Q}9irbpMGNE@habX;^EFqhNCd-C(XM`+D z6D*awMVWp)|^eN*HFj(+Od1l?OL#E+!-RP6-)SjPFx1;8X8O(b{Mh z4JLyxMcgO(8_OJ+87M(1*~Z*g>$nal)BE%XX8Fcu?O#BX zU^JKzC65pc<_JSrt4-6BB7^o>cY2a&FvEcJu|nvR8r^fgr@5Q(phlP4`ykqX?b+6Hw@MFn{|LCYQ8#O3}QHelWQi$m)h91UB8LF~{-X=HuA=n;l5y0|aI7r3GU zzUU(Sm)N2k9`xVfiVHudiA<5?uy<%5mLZxfki$9!8nrtE7@}o;hG@(JZImSf%u&_n zOf~`8qZz1rJL-K;ONM?rPut z7zab~L%cTd)<%~sL>JA>%goEk4%)#@)kPaLbnBLd6*Xw`2CYC{Q4`t!ptl;E%MjMN z{dxL_GEfpEkhd!+)3d{iV0CPil& zO^y})Q21B^>MufFv&6``{yPLc?m^_Cu0A64dWt?5244?<`FKeJ`);ju4%>iQnJ|>j3s-y)j@(j0XmLRRZxgLEgmw&VY= zY4iyVtXa?#X!%kc_O=yeu!u+_%gPLrNKMtrn!L7kaw@i|s~jY}>erJQDWy3j$pi&t~&+7j+vvsC(CI zuj|%z(4ekeuce|FQKJ=dRS2J!Zd5NK2MFasn0|?&=2i9K_ra}e0PYa@N+^FM2o@#J zH(R6jLC~_bp)cN@DC7tu-q2pIiH;QHNn?Jhj6`u+hOh`BBpF)?v5aR8Lce?pnU?^F+g zj7=andJs7JjJm88TH}SZ9}usgiL3eU9H+w%2bAafU=Ul%VV0MiXxc1_5D;2;eab;V z=vMjS`an~vT@(OAD$RXOw%hP>FF8=V6$X%)nA8-Ee~kw`x-b;6hIm>-pz0jtTMdFu zpHVL(gJ7Je!nj(c6DIh%^OK#9IvB$)SrQS8vc~EyS4eI|2ypzPASYuGbh?#3%6T&) zDDIH~*hZ*p7>Dd4NS$Q6s7N+Uyd)Azm9h@iI}(C!ihy*YknIp39}^Rw5}%Tk=w&cN zSaJq+cMAb{Q=oq>2wX3>1*RW>mfr*3ZU&+y!HbEo$#HSUY*jOGLx8PT&E+7lFo1Yn z67Q8vGQV~?;7Gs>)oA41U^Xp*fb&!b@3KxFjWO_2eWf5hHr=YSdl-HR>LL^Z3cI?l zQ&127mCA6iIKW!WnK-jHojVbB5=_No|SAEyyj%&kH%o&0)kB zwF&VZIQp(1;L+nfQ$P&tFjaYFZ)6xPTKL3Ca?)jt@`{q-z6xpqA8yUzFr@XJCrd`IC5 zcfUZ1L2D?hf-zJcBIOQQ3TWJ3Pa*5ju60>vrdMWA)=?-v4M;}ciZ-qVvzarhM_e-a zstyJ1XWlom!| zP*-iS2f>cGy7suP1?uM$g$K1dzQ{QyQ0fp_pklvRSQuDzDxPIB zb#w6dE^}yqQETk!&s4}qe??L|EE5_F>AlbU2iT10au8>?MC)}zB1=@LBO*%f8BMQ7 z@wYyQ26kGJj!#w7PMz}7-Rn#E7Z&~i-zMf4wVU-=&*Tvu*>%&{S$nH?5ow0vPL2m3 zmb#tatbBUQ+{&!8W*dLz+#3~@68k+C_^$9AQAPsaBB9Wo2UASQ!b*#k@o}PvdH_@v ziX%vuqw4K;Lf@hSS7y45oyhc5af(b(b>OLNk5QZ`5I|`lz(X}^OO`2=$6!<^wj?Ur zmHgth53Jn$@#dZH|5huo;S0B~l%AaQ>Q$vzje39|;wRX$dCOm9Eq8pa|Ei|!zB})o z&d=`J$@gB7*<#jB<9{DEZNT`fb{9EwE@lZQ+1hDs_RgOB<(&X-a_vdox3Kw~!sETy zWcIt}4U;N)6Y*ae5*TZP58d$!dIShMOykz+G=l+)pc;hUESu!u3uGDX5WH(cMgHzI zR3d=;#p>Ye>Mo%*1CgA9(_(>C*AnjS8CpjKubG)cNnMtVa^12YMs`iV$d)~>*K<3V z=QKa`6Iit7X)-y_M>scqFWqtOyrP^Z9e%UtUFCpUTbn7Ax{J}q0drK5JEU6M>ijv? z0x#`DQ>4+pK@eQ2aSoJt$L3_}3~>ck-?ZmsW}_kX**R8;YB|-;0U{PKia9qgV;P&i zU|DmXx!Cn}||f zQqP5OTI#jInub-b&%&VnTst*@w{mH{yxgV0m7x})gh7ku*@Mm0)ymDv4Mfh^MC8c- zb0M@mOQolpgwpxhyD>eS2{S!4%5?0LbRpZF0gIa|nUJCn*{V%&?ns?Y3_4j7(ix+-(KdjqW<-*K227SykGpZG1mAlqHp%Vng4>9$t<)*yEHv z(8RKY&YGLU0pX+-T0tDqAZGxo3K|=nP1rEOO-2T+&B@8JWUBV2Itdwa#1ZS=(V1da zP%{wP(#xM`B?qMh{_w3c7xKNIea=68o^|WB=)TGGdi0oIeec3v;=#1ttk)LKujZ#- zOj^&kZ2JIeW!eMZ&6x4s18I{F+;`srun6ed4!*xjAYW~yMVbI}1js7l<*~Muo@tFykL<`N|*$^!P^!7k!T!VDT ztM8U;2!DC0&o}4$FL(>_A{F;X)O9X!=L#~pC9uZz@QIK2uczjiXb1ihTzuSP|qXjgMYO~Ujf{i zotCP>&%%PQNgIl|G$?IIp$IJ5Y6$R$Lfn%L!L6M1(L2CbsqHC^cXOyWRCbD}lQDu$ zCsrzGsHKxpb)*w?^|>THJs#9gM|3RS%h1Ngmm$by*$^_~q+sV|L&$j9JIIqszE=$& zS_c>W81OD-yMT|n8-WUJiSVcpA`U0<5>?$cxFjJ{5*k8+?Fe*kHiUwFbBL$Y4?ldJ zaY74BIlan#c>>exvY4!7qAx^QsfbP?mGMk>2{IGGR|fbwbr9(~6fWuUt3HfyZ`rbC zo0e_JoY!PJ`2ZW*Dq)j`>MDsE!dJSfzE{xa`)mk%{cVjnLcQzgr^BW0%0N1_F89*` z^k^CxR3|7=K=aTvivo4-&;}{cxjqHz{Gv^zL7kH{;JljyUPw`+FbSdpPR5BRdmZxi zBsUH|c;xMxOl(Av(UOfCLPqT9b(}T?w|tA&i|RZ341h74qA=h>w7*hD!)XSGWj*-F zeX4D;#<(!Zk=hWDk~Pqy+7P75CZBuNuRoA(3cm?pg~l`(Dg9*`o$H{pMd(ZtIzffm)~#BR%GRo^Rat3CsNxpc zs2~nd+(MnN4WS}E)qoeaA?)N|8gj|{*WoB|ZYkWLX|P%3YcR@+5wi$nSx3FK!s@XZ z{_6xCu7sn0Oue;3bF3DHsy|i}c;vurXuF5?%r-;?mLA?m+Yk{HN2tTrx88pM_ZH~q z(w+KW1xhwa74D%j9gbSc$}t%0tM1*pcJA1=LiN@Xw$#d{VQj;T_Xhm74N*br-9!g& zLuAn}zkoNF^wkBPE&9*k8%P%NNE?J7TS!TYfqzgGD=}U564glnDcP})1gpG)!HJfW zg{zb8R@GBrDP&@Kb}T%tq8|3F(?VFvs-20#^iE>A+LYKXJEw~xPv_fv0|3HoEO=Tn3~g~sd=3AO zALh^T5jw%sa>H*^tcvB_AIUEqyXO`5=(4-{tzUk?7oJM=OcU=+WpmjR1kWqA-y7Bd zo?NoDT=3=MK=gKoB2Rp|$_~mf66>W(UsSeaShHatpeuq)C&KZqHY5Z@WZ;8(1ZJ!y zR)7VY19t}!pJLc!tm3*6InT_v{c%<~`}POt<=k*RtN4=fPxzn9zVFcfhh_Xvz88w* zB7XkIN$2?S!+Y3q9=GQ(i#s>zN2V;|FYv*9G9R+|sizjRMeIKI93WKTM($27s!%(* z+Xq8V0c;AzRE0qyFE6h!uh8zrMD1_aWK3Y*HW3?XzGm$<9Vao`Z~tnztpRSW$oJBs z2vKT6kantGK(mI17YNw3%4_9CPHipFWaNAXauYF>ztHUErei1@{T8%u{Rqbrp{=_$ znhyAIgv!$>ioT^&JLfhTJHLV5MD(N%etWy=7)nq0EJEyc$;+b}j%|dlZg=YzMUoFq zgAu)f8$~oPuT4c>NnVMcRhzTvLX7mLV*O%>&E9m}Wn(=nzK#Z~)pc$oba1!DQrFSo zLhPL6TWqx}V{;Ze(9mxp@?u4Z;oo%RrP;oP*IWUCYu`$!Ag!cz3(=@ALhn=B$x@}k zBGnXsL(@qh#)Wty zy!gZZ&J{WdJ%v&35&6tyEN6N{8H>k;v>fgp5=hJL-GXQ-G@q1R+(BeiyE*M!nn*_ZWPLK$xj+6sAp;#E zzB70Zc(R4i&3#c(zDQkwNY8-oq(>7egMfw+4a+7d8!hr$IQ-`mx^Op@PTuOQrZJWD zlh=RJbi~DYe_k5)p643F8pPytA$i=w5cj~29Yn!Im|_DO)t;yVikW?z&7c?+gR#2a zChH>@je5-f(d*ml`?hJ5pV!9S#@(v8MNwWweuaO}HRq6q=fF~Gu_1${6geaQ-pNg6 zl4^fHk<#YsVrO1ysLcrJEdliEB3$Rb2F1pK^nw^_F1hma+M!W#5UHAXhy|01Hk6i* z-F#Z{Ie$a53!+z0dl2}Xg6Y*mxWPTNTgMJkAju*k#byxAT*qTQy0_`lrb`IvqMCL7 z2a`_vdzztK$85EKTSp9{Vz`DLK3bBVP$6KFwJ_LyeYeh?f@#T`Ps;qf9^LD4VB|j_ zW1UFXhRHXd4?U0lZFMty@Zf?-k zM^mX19i%~0G)4FuB=r%rV~3F49{mr<84_S?J~{dBzo!BRQL|o?3ok8I9j-#?BU~*! z?4HqWriiXA%5=r$NNe}eRac4Wk(b2aW5E;^pcQ)$YQZc-n9LEQQJZIqiZs>N zj@ zCg8KEzb@0=Qv=uKsw?ZQi|}7rmi)Yamo~5@|3sXGUy?fE-TWo#R)0zUj%MEIq6Dhx zetnGe^a`P)Q02a%Lt8N-KlMw^apI<{{YvJ3u^f}xgnu!rgV=^5OH|B{Ax4Omh^(4Q zJj{B7c{G`U#-XqX+x4kuD57sA`gDZeAsBIwVAPKZbNv9fty71#aPpNDr`!CdWOLj% zVFRyCWIAzA_&97c%#Nr6B*vQ~j0VBVtO^F@QE6Is>k2DV=TKTv9OKYQQ7BhHypZO# zNh7v?%ZjpI_bg+b*e14_b@IHpsvwztzNxHZ+tT88rJhBvz1Zf_?rf0!Ow9sm`1yhS z<;x!}e(^Q&z7aE87qw|y*!ls2eR}N?U6i2%;;Q*LAt^~)(j&`@I4vFj}5+$yBJ#xF9I$4j}<5H^a z(Spc`cv?{K)4BxMF$#!c5Fi5PvC5dctN5WW&z?-M_GS?;tY;Z7tmfN3<8SeM#8dfw zGVWN(6E+<@^w!3M(iesfn+Lh{dW_aM{MgKw`1%t+^Cg?vo9u;u%Zbq}>dZ$h;na^z zs1<%Z#gEcH+ku&Ao3L(CD7{IeXqo}Fyp-64N-bHcqP`S^fmAw824%?5VJM+Od_OHE zA=9owN<>m*TO^n^OV(qbWW3IdLt}n)1Ki~$u8gXwZmkR--LVYRli8xq- zqY)r^fuR}_i$&JQX+141@1!@htbk zY()2f``jig`o+gG3}OIR<>CE#RKzbUlG-dn9Bj}qLO8DJod$xj-p9l>lC8(0gKOlkpKxkfI&6BQ%R zvfST}w79g;=D0fkve7;Y)~wvUn-Akp9OWmG4ka5+_ufhX2&KH6?q!vC>{MGtlVkm{L#HrZl}wPN_c0mulA^d207K~<03;UQme>e=Y5}^dx)=dcs)?Hn{FuS2 z#49|nv+LOxH?o3J>4U^L8qTg6IMP8otI408^2h=!Y4Q!g5ZdE z#K$><8;|!5T_oytLM;lj9eWddBeG6%jZDx9mK~GtT8bg+42aizf#4<}Y;R44kWQ;nL za~vztKYQ<#H=n9LckbTF(ITwdF9+?M9jss$JI^M1R*Bd0M|rEQ$SJy8yx(K5`AHnNYglL4~0$uvK+f0q?7jacH-Bt7ThfX1I{m?ncJXe#(z}U*D%C-*Dn_&uf|A=6*JH`KJH`IkViN8=&GFBaD4RLs;9Q zF2WlhctN4!C20uBI0@2Or^_V4>=Ja6ZhFHPqW_rN5g926kui}m(NTVinL;T>yU`E7 zk81J=elVDF+OG5RtL_fKjft!7HV`9zafh_}D&VD8M7^5AL{bm4a5SNZ;xeDmv#3H3 zuPvo7pUlg!WDp!PK5xw)W!La5_AOt==MDRO2EoB1`R~3}*Cxf#deM}Q@Y zP60H0tD9a6sVp#@){Le1fwtpYOgaQq$!pLj70`AHkhKHj(yBgRy>tINY4{4#a{kYeOEkD8`Y6wFUoyI5vvI47W8hN z_uUqYAXwrp@jeI);ULKJ?cDmH=rXh&TMwc)>vpRK+iBei6*ikNyZ0IkG>fb{kPJGU zd*V>N3REBUH)Sw%x=eLE0;wVCbr-9&^IeJ}J^z*NR4O~cY>MGwFBZ{c&yB+r2-_?+ zYpf4tV_1|y{V>>e;0Nmy|8wsd1N~3`FmN;r$a};NB-u=MM0y`+a8Q;=ID?%OY$~se z%wV+0at`~2-NL`*ExzKd`4{XCHc_65wH%Cfcjkca45S$BIPP~rv@fCn4Lgj0m zBse;&AaiL4QNpdp)^OL6;$k5HR8$FFeBlImnq~6Co+|zYcI*`K-g5$JCwL&?3R$;X zeRi}k+$ZGl3O!yyWc9tGk6NfX*eB#taI<<1J|^vRc;K~Ie7D04&Db1mj*f~1+!|yg z;esFwg-)l+wg3pvd4-!w1N|<*8L3tV&|$1VdmvR(l_6LPpBaKnB2_|INRkA;&t=i? z@_eIFf@O(VVzLG_S`b}S8R}WC(%OlrD_SE;3TksvYOhfym0qX_auwc5l)T7|h(Lzx!tK2W=TOp4F{a*HM-JG0~7m*r705bYeAtlF(+U3I~9HLB+o63fasy z|N4;r9Y`-f=LWKn@{7u}!JHJxvKqgV4WJiXu!5mxYt|x~eW)7~(9~H>ba2zx4*3UY zO~Ww+5_0xDB$RfBC{umUQSYv^J2jrKdydunoST&x9>EXGoQbr!GRH#P@A*?d41Ig- zu;z1wiC%3RfTe+}bvibbcqgxqAwtyawN4`WeW-CTNkn&6qnD`GV47ds&Oue7BgK)T zY1|Hr#bGh4L`~Py!wFlTWp7~?RFzYixeJ{m%F8eyN*Z*CuU)r}^*Y1~4zZr=*70?R z`2NS<8Z_uFwqe%hfdeKFQ~Qz-{7jx&4!W&3^p! z+_|S8pZ(X9AR%a21%93j8m3@2ttDA6LwSNLMUrE|*3`|JK#4G2zEvGP~WtL$vk z*my+11v1ze$_pNtl9d}9tDd5eck2IBm_91NibPwL9Enx2FL(jWwBD>Q>%lWw;zc7- z6|(CoGAV6WJNiwv=}HSv@YS#r1MkJjzOya@tgbGe#g$)6_k<~2AnKBkK9@VPy^!gW&jcf~+RaPGZQmZ z^`7C;YWhD7kyTxN6&7V4Kiq54)hJO|!!llZ`kMKbBs^HH)xn%Bjh*JE#j_oD(L=b? zITxYZqQ2WH{L5{sd%LzGX#|80K&ZNo21Fmo&j5ud*>{Og@d>z;$YQsl&xFlsb2{uk z&`H|~2ReOLAAIo=Vx({=ocmeTdT0$v7a$tOlZ_M?Z8pjbIHc-6AMX0y#~ zO}AvFsibtJ!O(=$0WSngYJN?2irvW)Sn&*gZj)yy%VKvCzjB&7FBgP~G@1<_Am_4~2DQBhHjC_AkFtTc7Y z2fRJt{Zf43`}tt;{a^^lVhVsk_4Df2s<=~UNocz8m%%qC=)goO`XofbD~ym}!StnVuO`2A2mL$#R7AU9Z;!C@Ei=>V@fSxhZ5oKm;p1Y4=gXRWKZuZzQ{m>?3|^qR z{C4gNK_tZUvTn@e?I)EU%uJA@2tKV#hQ zx3Gh_H8Rom>3PMjF^JFX^LY1;&+_&&in?!&HvwMalPWDHlLFaeqItDUYdL}~q}R(s zR8E1jnfL>c7!=6>#-L__VK1xCAK(s)${+rNHcj^Oh?n7sK}f7UO)Nnoz{ew1(px?r zfx`T{Sn1hwhJ=Cms;95`F)y1wX2QQ94gk*m=$&$x&LL!}6~+u2GNXBhIz=Zd)%cAf zLuiMMqiiTd1l*;HB2oM)S3(b_bXQtxQi9WAwP3)Kj8=15Mm<7hm@w~B`^3q0eT~Xp zR09ZwvjSn57NU5l&*KC6(Pb=V;UQKue?H&$`OEybr&&V(SyxSb_PR*VSy2~t{Q@)` z>oKWv)~}C$$`|h`*~6dTI|okH%OAOl-Tls(qhmJS0vtqPc1>1C|Rx)DANXAWQDsB%;)l64W!kmTAuGZnnKea75t z*6=N#y~7V|i03cy{YvM@*I#qZ`p0LjA20w`m*=)!tY{fj@lz-GGus!l-1j{*A=r*Q z@yF~Z{+#o~=_h9YIUDpDi__&cugpgmUv&6anA*fmfLDQbnm4ut9aR;%QPA~B@FS!K zn>L#)#&~j#;b^B0fSGxWR% z2h@hI$mLhZ)CeV}m6DseqBXqpARtAJ7JSBfZ(>t_UNq#f&5WJhx6}Qv&HTQIT~p5Iwx+o~ngv?&Sm_!D49vLmsP0+fj>? zM#0&gX>HwtdCgZClV`qW1Kw>ndB?09+11Fi_-yJK&AjwH^b9|=@6xx|Kkhm{mSx`i z`Vh|~&ERB0N5n1e1plT`%+rbU*vLSSO7i*mR!E{frv@w3eF&BWWyloD&PvxjFLo_p zQK9Grm(S`e5u6G{M7mBdjp9<$r!U_BN-9gw;;(Yg7kulgms#h}YS^_I{F|f~XRkhJ zmxdhO^yQ439eer7580@nj>^m5{bbbm$fgsW;NHzxkkw~xoN!5YEM z!|8B^ZvFkohArK3h;Mk~{!^>OX>g00ypZKfCy&AA~3s}bXME?2t zIea;MN9c}6oI`3QsaEt(QRr5CM86nolMG4T(cKPyVJIV&q*x)mQ>|z4%2O-@^Q;Xj z2dRQfZEl;d^i7YA%}U3phZrM5bkk7X}@hOgrP8S~XI zFK`zK@TneKJ zO)Aj<85{#q0o8w~*OTmagEs z>^)>uVL0GTYgSea1^%oE4awKe9@^DM`Ynx8uT z%;cwIJoh@{u9)`zLx1wuZAaK|*45LdO)Wr!f-7u`Cth<)PtaEs?#H?K0sM2+i2=H2 zij}NdAvw_onMTE95W)fztBIjR37HhaNVrQRsY(qvCJ9MSTY?gSld(M8+k}De6f`tR z(ZL8UyM!(b=q@1sv5g;}`!tJtd&zOu%E7<1&lo>)W*p0K@9awPThH&cOuz9H{+;bV>}NSMo&R*;3w{((0fZItO17P>U4TTc} zSsOS)M?sz<-G_2HB24f}jUnYmP`IM-y||UCHzhNu6@_i?LhS zGx(~66Qv*T%Uswh7;ZxYZ!k43!qv7h7ApNp6 zJxQO1)0O0nolJSBL#KD?%FoRhTG7@f8ooVb)~{q~%id$Ihm-i%rpq6@{HlKsiDIAe zQiGgTENx&WY1E@_kHH$dZ7)lh`655Hu*I`hUDQKte!I zL;|xN;zxe{y66hz%v@*hKk&ETAQ<;{m|oD@(Khd>sO&j;Za4Zh!-;P&R8LS^oqX&J zQ*2S^b;h3`qxGAK^F0=Pl_DTVAUP>9AwJHa*CB&KWKm3_kt6WLAO?h;0ZEK=j&=hW z410+J5#xPINvSoS_64(KSrj|IvDIVCT`^kAV*Hv_j&F;&m(AdJ(D#!+&0rOK`Hb`Z zmz48gSPXp|1ip0<|7+p)h5YP7kC9Df*{AvY{QA8{gEVP8GmP7gf5x+ow2yCsWB)S1 zO*SOzKVd=YA^@vis8SfR254JUA&JE>7>6IgRd+B$Z{@`JSOmf&%@HPKC#iS}aV*YE z*?!&)p=}EK*Xup^`CMG`Y+lK>@s8LTAF#IcEgt`owOz1~f8ZIx)_k;W+YVkSrLW{? z`QvOn1E1=ogUk8%FR$Up7Bk)U1@qDB7Bue-n!ls=>qbwvY}8WcU@mPmHVk^&NOGka zbF=l>;06JG_A$Q9hl`rBJC1NsH`RE`X+oc7Or^?9N8}U7IsT2ro?_TAcAL|scR6Il zoYD72J;+@ZrD(5Tkx@~K9w#NT#z3-O`Dn1m3}tQ6P|r7QnSK5JpH@y9!;jD7AMma{ z`0?JaNfPhnv8O9!DgZH#T>fpz0 z2zzMsFi+1BKa$+K8ggqh)+tfA#N7j_jijLH42nTF5n(lPI;>5D4$V#|M*wnIBHKj5 zRaXN`+8GkN3`iyli53f7QW&9TiO>;(8Hfk#u{&6fE3Lv+K?;h?>e6R9oXV!|`%=)K zPn_YbpS$Ld?&4eNTbJ^L#SC_SoI%|c!-=AzQf=`f#1%A|6W`C9>fJX(~atA(f-6w5^c+~{c4${X1S#{#lP z2CK%+=suCmY-U4^23UT;0TPU4UHc~APN=KTFbqW^kzu02XIFP?`XkI{@69S9`v{Sd zbq{m*yU=sgG_IR`SkZJ2$mB*INmJZ%m~c)1eto-k>Cm=yc?nwmW@pAbV`DT)fKEoD zP{ztEBnK$-&IQ+;>VmOr8kc5LA@ettQ3@E$t?V4NCo+s<3z*RxDyYcL$0RgdEMl`OF2KJ1>~|xn!yN8FKT; z5!REh+cb3i?u{j#?iq1KuTe>DTYUP=NBqw#dWHeOgySIDv zW7Wp(SFC#fUIo*|t=Orb?sfq|m zDJw}Sdj{sD=}YY{oeV8Nb%K_wj!?L~)D4YHQ9E3s%!Op@9GAX!9~1ZUnr{3Jewq*E zAD+hl#62vUUD}neE?#mszX37oM7HquMV?C}?E9Ksx(SCJj-BBsr0^2pNUbO#c;dwvt~Odjy-k(rCa;IGlHI?Lxhx9gp2Y+XZ(Z zSfpZte@zi+96(+YBVnL@A(;uX-*d6}6@SDt5FM*`@m*wQJTLyr=G4^6JNchAU8HIJ zsAsP9Hj9v2*X*QkJQzX*=9{S+u%tpirP(VjF=?VgxZ6FcoEc56TBtTeQe1p=WQ5LW zPq11H2uK=*QMT9^L?;uWEFo{4&b}zKXfln2x~`slWo5<1nVFC>6=fC0rNyO1g&59~ zo0*fDgLH6s^zgdW6z%kfzG&0XBOH^ph0ki&x2jBWGU(S*`eYN-%R`?=2w~}>Q$n|JM9$NabglUR?0VBScG0H@#6kXM4MQqAi4ixK1_l!!TL`)1ay%n1q~IRZle-RFN_c zVy#Y3EhqkOU7pj*Py*TQStjvp8@yRy&B>rCu>j_ET8cq>Va&y@~BG~aMvyUMLAwz8`P z$OJP2ssZw%s?2ca8iqongBAoOf&63R(R0q3r!G`lN@9YuMO+JuEj>0XCX%dD7YA9q}VQ zOc|IgI6A%ZhuC_U#@2$+q6m*HuvFM$Gh$0^5p>i@4&a=f!=Pwq!tG1uRgOLbYKNq^ zW)t$6t`cWwrgu24hJJPbK=ly-h z4jFO(U9a!?1uY7VX(`=jRE`}yq-HQ%@De{gZ;*Hk?{@7tw)tA4z-J8LC*FAUhI23M zeV6UzJ6FrcYvNn+4KkZKm2Cr+da}`SK5%YK=Qw3a z1=()I3q-FJ_$}S;j;YDS2~w6l)`a*kd@!&8!2Z?n!{8*F41*J{n5)U|6j2W&iAcT0 zttob=U9~vt_yGPFB1srdaLP|^WgS0BEzYVi=Et&XSfAQ&K*{r1CSH5I7Y}nx^PqO*Uu$vA1wSey~ z;``rT$xkf$^Boo+?fP^|hw<`6USo9Lv2Mue#P?r_{gfYA;9&<@ch-X)RY-T9zUi4XpaXHBTcw^`h`~60rnu&CK;o{JM$TxVMb*myf04x!@08UY z56^;~T8Jv;a3_>g&N_W5_wZNPr3grU$X{c9=o^`etvpkdclX`@=>z-j_p}i&o%-SQ z{rDEkmX7*R2z$B1}+chO)j zhlyZNV1+gK*GR=TORRzf*EB4(*&v!nV|%L>J5DXs*pS!yraqA-5ys zH}z3tubKr;L4h=H7r}QeWLm5lvDQpmMg%DV z5^Z)^uh=rGuBxM-B#Q6S7&zKWUU}fO>aH5w`u<+`?Z21rzw+MBGoRs~{m9R;ljcXq zPI`;)XSvb&2WQ_fzs(HYH~iO>tlQ4MSCb|k9a~-T`rMk%Y_8{R`K|kJ*}S3#30pr7 zTG+D#Lo@`N;TUuu6!i&_h`>(Ja^Z4d>cihk!k*52#2S#ns{=YP=ph`3fKsXfljtr8 zy;7%B9kHrcIpk`%Qjx}#5EXAlL*EQ*8o5%ft~6wyxh%leWl`--${xZ8F|K3MjZ8et z_#QUmcFzI6M@(eJV*V|oSusas_57cnqhi9Hw;;FCRC8wH-BN80<8ak7;eF`fR{)=Q z0d)cPSW1T=_ZE30fRyU%cG&GPB)jrhd5lQ&<8&h8YvnknvB#);vqgUe|MZhJe9y|y z_`a7}>s_pPH!E9lnx(RhZ+nz}AzKdwRfz?;s?({?#GCp+mu7?YCTJ(BE69z| zWy2Sn;0J^22lwcQg8L@w5mt*6gw7pHiVO2oljD)E1z2KQIb9KOgTZ~GIZ*X5CDJqM za@Sl;^QIt`0DIL!8Boy7vm9zRSB`(+pnOF9{1j`qVB==?J~Z(mCKJD&&G>f5&L8;Q z#t-?g36DNzN|AM;PU z*KCvYHD_hZmQ^qB^2{#oGv%fMeFvtr8Oq;z_`scCepvYZT?3vdZd-hC@r;iqQZ3pp ztna0uZ;=3fH9I3UNoM*YO7X!7jy8k1LeECym{&s}IKVa4Tn$jd36h@|n+}ghPHcLN ziMC&c1;J=N>YLQykX))BUKQD5d5lnDDTTZ(aUu)MAz-SRn4?r4Y0;`x(UIXdPh;lo z;?HLvV^-aaIn4Rfkru_pEsvaJ4*uy)Bcx}=TXlNRBVuANe)gRY_{S?bqQkLwu#pF_ ztrz!aF<;B_*ZkBq{1X1w2WUh@`>=L34!_TUmk~Qr&A)=*RfHFZ=Egx!Ayd)IC@4Q8 ze~!rnuSBE?zB1fnB)@` z@N){frI3U1f(u$-9_AaMJ6ZucCgH#_ftuJbXaj?4VJbF-^8ztbbVV|eUms0FDGgT8 z4ZweNYSO$;xGW#KSAKDRv1a?hNKZ>jqzu#)0xw$Ut1bXVWtg`*yZ4>N7bq^}k(IQw|5AV2Myz*bhz{=_puob{_Xo_U^#SoQFoFKT;L!=I`G zim_Ks5wI*Qz@I|Gk&yl^5wt+)X+oqyBwTNT@7ZLGfbk1oty;l`ZEG|NgHczZN)~8| zbO)$LH|VBc=z0`q3qo;={9GDwM*6eGqLRuIrMa*mj?_v#3zao`$%Wc&D#(-6otd2h za(S!j#X|lD>%HXQ5)_q-D!EYQv~$V94eYJ2-&RTX?EC|-uH)C%QHu%uY$`wap^svR z`A2pzE78%oa?0#aeqA%CdIi{1wGY11-3@&@0=|h$d#^ErI!;JcHBv?qXgWgS5uiA9 zV)(yIq$Fu;q?y4}XLd$9q3}eo2puUrXg`ne2U;CK*(^axxQHePQcSEg6IIo>Fr;A( z!PiWFD_^kt#L16$pI|4_nV8Q`vp>W<&u1LPFrEV}f<4Kq*}d%F5XDn?H6H`GneaRo z`eW=Xy?|O29a4x%oyDTU@q)iYlf}OjCXgbii$9fjBLM#~-)ZI!2A}I0LAR4{VY%=z zGGQBdzz7;7lTQ*6Ejm~bA3+$R8c$JGe=QWG4)Mbt#iy8fked;ktE0VHPIC=tJfhC& z!mI!-cL~D*7$S=AiEoUX-E;4J1X1~+m8|gXcUaD{t3KJpM@#d*5kJ25l||P+{n`gh zHzMQVX?}L;#LxMkZ}I-o{4cBx!8K5YOO3nvA%h!Isa7S3L=q$zM6Vc0K_yp;E5((T z>H}33BaMMdlBF?lYc@+yH43t{U7Iht5x%D3fV)WO>+YRR=?#cOA=g(YL-nBQ0I`~S zPJqy4WU1#H=&LGg8mBQN_a!F8IS@XKG{QI&idd0R)u1!8X>1|%VbB|i5cmPUBucZOB>ZC>!y4+=8W2z}8EXL3Sj$z9 z5FJcbFsFT;~A zyo-RsdZxSO-AT8Xo)CtSG9;$x1756(FnDM?K^&R%F97Q{IAAWUI}0 z90(+mdV=vZa*kF3pyt_H1Adk!ea6>rU~KHBTmHSCu@_nH=1na7CB`<)y?MhphJGaL z_P+&*apJW%FyiINmZ#rmY}@>WTN!&lX)cqNP5EMu&74`gbiwJz9y`5Y8P7D^=6o?_ z8TZWfY@D~~qmLHN+bFGnefF%?t7pxAo#e?2!2Mc7N*(W@j|q4OeoGN2kPgO}PKH|M z;~4cUQfV=s^=+7U7?d$Oa|Oz>)1+psH3J3!sFH(Y%1Qwg&5@7;8&w+jATu7CvY9VE zwU&SXij%cUS^vb58WwXYKfU-B{?!V$hPe;ky76CZ)Vf=5eQN?3#{kaxn$JK&@;q+f zXCALzI&}$h6NXNi`!Xxuwv}~Q&(Gbw?e^PABb^k))?U-KKsCOvD%^{7qz|!TaQOvq4XxgdXTA zQ7ogpq?1@7&E-2S@!!8ZVZ|+-F>eqG%iy9LdB2-RQ0^jwyg@D}o|{Hp3IcLDA#%bc zaYA|`y@lLi`Vl0{9Rc7Zg!^FczP!DpOw+nFM_g#)K)!I+YQJ3D4?RsbydP^ zS#;iXT^CMCz=u3Ccc?nv0X5jSbX6 z?H6Jjcq?cI#ZD&e5wE0#X&j75smjdC&af#F=um`)5qK;)ZoECZuu-yn{l}nVEEXp( zV4tw=p5HSs&E3c{UtG<1Z+nODdX*LKTmHhneJ?EEC(eA5bvrJu_Tao~PgbkeLNPvkIG6tpwf3 z!UvCpz&39+cHBFx{54U$cRnxX>&2+7zPTIu7t2}hz87BDzyF07_9^$g$6Bmh!9V)& zNxt#8=Q4>!#H?V2@4fNE_piQr>?qy~_+F{~N;ebm{g2uMi~g}az+Of~>vODmbL;{6 z^RV^+VjzO97ijP|?EzP#O1AR14FaqW6F$Mdp9I+cw{>eoE&h+wt!qHrf#9M4dEFY> z?*BF2S`^-cE=n=7|7Bft&Ht1x`V01JJ9Y0jQy0BEye|5_caNpQum8WTi!S;f)!OeRkLaSWKo_0)|8ZS3+|l?qbx}&@mMI?GY+Y1d)u=8?`(*;2`~R{o`u+tf zg)XRz!e@vaD5?niZ|I`WH%S+zGo=UK`CryWJ^u||^epIcG0utqvMze|zoUyv=+*lj zrkOdAGf;$FuE(`%M~g#`ET*g!C#+)^>Ga4F2>uK{7hh>)MxEC{b%UX45@i6Wdbaj6 zxwX@Ru2S1HAo51KxYx-5Ba#WN%Zdupkj57sNdbMm<~M`im)Z!FmwDs8HeED>34v;C zQq*&FGFriU1M^aa8vCI5ugxJPf6PBy^!e(c)l-hofA+h(W>r7J=H(W#sL@?m27mdX zQT*GlczNqP2JTq4?f1!748D8RzorjKp3CGVYdw>0JNn?v&+oo#*B!IJ9$3X4ecq_r z&yQZsmpngo5Su0@=3cY1<+N?GX+XtY*Y&*r$pL*ALa*Ondr}+$K9s=yh3-7kB?rSS zB6m62LgeU&F$7nu5Mh*~6?AMCP+CRKbIB!HeK`w=R)`lR@ShTvTz=bam*d+Yoa>{! z_SeZ<0YRZH1PO{}1=yeG^I|(OXH8HJmIl3jARV8NE^oMa#5=5b+|u zHi6k6_R`gJR(VYv;-{)gQ^TFutF(SJ!TJdeidM+iq{d`$G0JSH&V&fa$UbIDdGP;c zREZsFh|q|gh`4jzJ>FC}?T*2y8Nux#Hw4sHS&;LW*cK}9FeE;w4?(Nl-OC_PD z5_$_Epb(B;10+Bo^aKJVgb<`k4b?&~Ukf%sMLlJ52V_nTDH*kMB}q2&6_lD*0jWacvo*f zN{jBQU|nZ*mz`BYZ&`sV3J=-O=!Pq(u&nVI@fOob6nlhpaoip_ISZl{BueRxlf`m) zED&WNoWX7Jh%zS+HpS3Ew8Y2xyU9@LfJQK{FM`})EtQ?>m7V+5E07y`Q=|t5&4d(~ zHA~v*|K_(3w2$Aa$%)&GANp$>$%#WR`MU|+5^{phfjRr@K_lYk6`txqD2GUR?BK|S z;B^GYLs~c@sbq9PcaT7|>r=F9h?e3H0|Cy@p?+j|n09%AD`hK>xW?Ae0<~*pH`xhP zh;>;B*_qIi$>zY|8Iumro_@sSCNK9rFe$yxbcehY6Q$|u<5r)3uP)l~I2Vws|Jk}v zy)~rEgt_UyZX%NQ?kx7s1kLSSH=xJ5sn`V8jV(mR0N+7cDBnTYOOB;YmS9mu<8}y1 z1i0s=dY-5yK-=y3aPj9nts9vxq#m@s4{p2d7oUJS6eV{1;g)J4);&G*$Skazwbbu$ z?6P~?Eu%+oNms8OeqTyvbHq8!yi6UfhFrU* z)>20=V`lMw8b`Pf;~QM#la(-(CV^dn8pqD@MJc>(DE8fibjwgC8UA}jy*BQd(W9Rk z$NY}`S4vVJv_GicTE@1q%U7?l%WT^+^_KlX8lQE>_+Marz0i<$fXI^}?HMv^DELc) zy@#t7is*Kf*VL{Y$h{uOI*aSo)oi+&V*LB+-``&?F&@v%gldr~>%F*Y3bYDefQSHM zCepaB`h{?(bGJPo8-b5s(Af5=G>7!xp9?dtVwS=TT6cx&zPkh52vP3dY)rIVi6)l$Znbj>}3gfRI5GrNIu& zMU^TocfGt%5D}AHF z11YkUhZ7<06!#Xu`d8}Z<-2w*XSGbZGY6SJafNsBICpbtMa1xfPL*u~)A? zxn12f`?cuE=+|beo3=m6*NAb1 z&K2rq^(*HR4T)EgN_1h%D)qw3t?EVf6Ivq6b}W%)^Cbeu8|^1eQTpS84Oxl1ZGMr= zC?o$~*8zEL9T9KntW3}v<+_ZDY|ImkI>iVfuAsCm*bS+^s7j_2)A)5a6(tkWOdwp| z^5&l6hYScVg5*cd0;ZrJ6cvbZJmLA_R9pQnotc2PgNa=d+sC(V*`!f}=+IhzzNn#Z zV>X^Y4^r_nu<$Th@T1&iMDC&X2ks705d;zBZR6VpA-xDKgV8<5PiiYhg@*~qOF$|* zbY?K})(|&?;9SIc)Dv}^?VIcqVEH!}??J3deRPm%@>s*{54Q~2I@Mx{{N_cwdS=tD zbx-{oyAO2*vMxQXp1iR^lA2E(IBdDzgjkn0|7G>JTm8p$$t@f^AP4y@-Bq^kCskD& zsqd=Z_Mc-~`JAoWE=scf^6L)=uJ6A8)H3iM3oDW#qYLgvMkA!O0vV0*4X*LAo9<$$ ziZZ&WA{mYGzrgs_kkQD(;BwR{qYGUk8bp;+Btw2W#=ArLS>Pr=(;e{DCm)$ZxK$>W zibay{jzm&nr4os+6Y{74@<@Y=1X5+9q!_L{6C(wciV;zGSj{)pHvGY5$V=tRkl12z zpve%YIDiC!FhJl~CP+fir#hvGUP!8KYghbKX(6H$4Nwf_J^H^EA?yDI5u)b*DW$%42dT>hdQBgL2dAfXo}C@7~_mzn761&X8m5nBWL z3%!ZF=1FBAbAB+@h6v1ZGes%!mpnj7_>IR;d{LG+zVzYzi@S$D8m@*7zdl_3Duy-c zuYME5V(=TgK;Ih0s9*JGF)`{l!|kuK3;oqFG0J{UjJnRgzOKEppS!L$yly`y1+cHN z=9iE+cc1bOe5tV%pB5bGK7jqA531*py2pmeGt@=q7T-?0~*shiu#*rsc->) zvTRzf=4ZXO>p+jh!F}J`^})n~m8;ZAAG~s+@8HDlM_$=CS-rjCS!RCb8THn)<_ zd4_q{NlV$c_sz`0)f<^^@`rmr7%+7Bz>oKy$WjZ}uPm7K_TB@-#{IgB)!)5`)!n&M z{eI7G_2Mq9p{;sCI%OCO@fBI@?X*F~!a&8DdhzN$Q`bO*g*wYKXUC1N*6YVifm#>V*g! zd@F!~jdPmb(gd-;EX#UXkEkr3O$lR@hGBkq@)~`K%?p!_d4(y`F8*u@_-U}^1Wal8 z#pCvD95FVjCybl^%BGLKFJ6$7rSAbh>NnH6p~UD($4LWYyRSk{E}S8KPq_?t`>Y^d z(B)NmUR_>3KJ1(q&+2VeepYY0eYC{0W}(W@nuS~Vu-m+M`YdbB=E&RR%dkbLR~?}_ z71Sf7k!L_iV(8&1_%I&{Hw7%h{f+=ZKfm7p;q2KT-aq$)*|R@jbDXc{;;*2i;&E&9 z3Wvqq2r5f;F{LOZSy6okE#PWfYin~UO4+#(=BRlQaHcLX@7xLN*H4(ZetpUL-&r?t z;yU^QnP4@!;>+?lX>V0&X`J0l+N%~KXcN!wVY%`q`8uSJ@)}Um8Ub$jqxb{MmGV>@ zjV}rx3YT^Jb#svBVR5V#0Z}3fOMx1tp;S^sz_}IE4=@^mQHv4Pq!7l3vWp~tqIqrs zGRzpIPPB!skL*-dUtFyIa{DrlIVd&AVheI{9$9D4<@24ze8(O0HMYgzZ%8VXa7spK z9-Wf;t%%5Qnx(Q;081aPgP=eL8J`Mu0j-f|#l9<@n<)gkvh?`v>C>lEMlv$VUq=@XrhHOAF;>d2vkfH`Rlqy!Kw0Jy zg|nFNxMRL*@Q!~4yn_;}rI`o3Bj(D}byP*ugC7no#IMhsETmfp7D8=3U7i?jxCc3N zK{kJMoDlku-XmR!EIJWe>yMJPIQpW7?V457H+4+W#R94Ed8OH>SaSz5g!N;igsxrQt z1_vL<4USh8p_|)CCp?TqW9`$$u$Q9#?z$B5s6Qt*sK zwbUdWC8BKMP(yeOK0CT|6<~YdZWh)D8UfG%{B9Rg6CuYmh?wsf$1xW0Dc9%t^cRqE z>*KA%^L-$iA(vAXZA=l(m5H(>&=iRIjXv{RKf^71a{B&Da~)5$cKVX6au>IHMmDTRwZCwH(|LVanx^V0_L9vF`}4>?~v zF`eNl`qG{hCpn+D^D#=}8Qp?7c8P^S0NOYhiMxIsBb6Sl0S!tE0?3}M{|%V(qpg=aul7(;s3u`jLpC^2N_Vi-3ugONN) z8Rv+gGsmc`dGXkMd?xHwEnzWby;KkfHA*<|T#A$C#l=9RaLlnQPf&S|$jY=BUbnyF?sYabRU@ z9G7MOZUF=6r&4s)(2|c6jPo;UsqhU!DF|6FLYmTuA+;z0wtn4Oks*=pX|P&OTJa3w zZnL?+X6v2TbdNhBW7yvqv8`jr(Hy% zqKTIUg%^1;H*jeV;EZjI{~SbwqFL`X8o`CwvQXutFX84i*Jf4axc(~XMaes&~;}v7k`Uy zy3xt|@^bJp^{|BO<|>8+Oh?N6bqr%5S4T0=xQ3CJyM_Tz?gQTDSBAH^JKzZeLL`TNj`1_$O6q4IDC~4r*z)HKz z0&d^zT7f74eqEkpK2*84m{IA6A&iZRJMdL-TF_24FgI}LD(JoR01Mbt{76zc7DO!` zORfu7#U9pJyjaKp#{7iVN*5&*mag)e9h7!d zP(Lm6jKc5?3e@t)lrH(7NyiJ@>RO6#prv+e_NE^@$XeNXoksYJh2fa14Y#Us_R&*i zWRN%azn9p1Yc1|J(jcK$iQycY`O>od&vY%NEf?56^}2e~TfK=NISh{c5@TcEaO&e@ z3w_Gag26!6bBLsnJDkIfA1IzgurDi|uWPBzfpYA=%*&g3p?-Y7f-_7}(b-P~PNyo3 zi{o=}>s?sK5>1EJ6>38DpnB7=|k#p4F=cxPGzLn@}`hwzx3h4i!cg8>J z=P0p;c32EVNsubS5~aWHIAj56;T_Q)QQAPj<@lK z4og80)qqm1K@nQxEXD`8tWu=?djy1eiS_K4mWcK6qOSdW^``w2<_{9LC<)ST>`i$# zH8=XP@r*V+A^&TzCwjdT>=xl6rKj#az^=Roc5mGz+Q?#z49pdbxW$Lj4YdB-?1CEh zHoM3!ysd_@3lC|(USJmv)35lZh7lel79=Dp2?`xCd~oN66CId@M}hD%NU~1Opp<^6 zb%M(g93?fcwp)NP9VH3OXg8VfZYL4=;-g!n9~cRl!k!2&HPHP*yq88;Q4fLc=;~1sm6+HdN+oWf#exJ&6})$eO+7Sq z!t;}RrnVV0Mzl-Q-p(EOLQaptZ3c`N?G?Po0$=`W{E*apW9Q6x>6?kkqdTpgE>e$8p%Z6C3%w*yeQVnIT~oTH#0^Xr z?ef&Ob2D~j_ZS?Pm?4V&Up}8TWbD0h_s`z_UDnXi9b?5#A0 zf4?U5g4ny1USOfn3)D;Wf2S9ys$4bo!q4UE1vX6SuWZ5YQYKFMk{?v<9-ZheYTU&> z8&+EGOLLV3`GLyhzC)mr;FKS5$$hk^?#a*44!Lg>D;E1~aQSkd#X#<#amamC9~KZZZvC7`pe78lm3wX=$+IBc`_vXkpto``yo|fL_i1V^~z8L z`{~DUWiX&*FL7=$n5v)wYSc0KKsoMlESRvX$N1_PWnG!hm{1KB!NFh;p5$TgOMQl08m zq=SI5ibV6jqJw@ft8dEJLBA+7bphBDty&{j!qwG36)mtL-6PfWqq)1WMvGhAAT#cmX_V;xA^(Afb25*7@hT$Al zf1SNaNEGm@zb;GAX%WIc-!5A1ztfj`*#4JjqA&fI_Ci%Hj8pCQ_rwkW^J4%Eo0ry? zC6ARq)4vbh@4!D@v+xtVup9||TKEr--=}Tlh7JqIzetwFoburCQ+FXj2qJzB{ z%EF2F`KwP(VRO|LQ`mU_Z~WQ#De4L~cZ&L?zm&(e|9JSFcMku^wyVRnSL$%b{OqXC zsMs)nADh32`L(PX3l^w>kx8L;=t_!T-5nRHHWS9XBk0&s&(O)jYIaWf1UX;+@b0%l z&Q~U<{LxLBVqpD{-GxH;=v0%3-9be%i2~n^1{oH(0ae`$3VNG>IK@=_5P1MN;&P&* z0t0X#+^$XQs8&&}TDEB3EG8O#4O&t z?y;NRp1)~TRL@>vYuUNd56~d*NGv(NHJe}DFdcxle;zS;HF@BjIS%}MBJYe}cK zpNlRS8yUiYrx?_5AJ4G_-K`jahCBf+-VAz*b9W+1D|rX)CE~c^0ZD|S?p z+p1-=CiUwE#017@2%;c&LXa*ZN)Y4Q`UzB3Q}Cl6d9u_xs;ys48I`R4JM`P<8GD{Zu!U#NqMm5@bBsNwo>%kEKF8{x znak#VgRYp*s+Yc*&*q(B_0{oDGvn6PSD&=1h5p;KMm{!t_+ulp9`Q#NJs(a=a^~U*R1}t81|ca^UgR%ohJotWyYuP|9sh~ z*~1?lh4E&OTKdKPm;?k}8n+{DslcZWZFKU0U%gJ@ctv7vCLR6^MtvrZgEEzct1ez~ z(@Xur3{28h*To$4sa?W3*hgqtA5(($K-(jIC1)dpIYem#I;p~ zp6cTi8*%>^OM%nJMvi)P_^i>(KEEH60H-DSx;bpDVYVS=22KV)yEC&vFB|AsO^D8< zwv=FI`~Wi};>^X&@o}wML^Y3U-mFPUR<`iFo2s)h5~C{N;TM>%I}azbl+2Bf=RC8j zS?5ko%VXeYnYAl&Fl8X*zXjDEy4q}JBxJ%5)z4%wB90k$ z!3qV+1tk`T(oe=a?=jr~G&~Tz0=xp?A%o1u!xpQb)z7S;C5(i)k=WW44u_h>R&@G7 z&l`E$iH&1HW1SnF0?<*;R#jZ40?+Cquga= z(x4guIn?_JMw0?BGaMdzlcHY;A0H~MqB{|`NECU1yg0_9GXrc0v8BXOORvvNdn)8Zv-R^Qv}0md?tbLT#;3v(?COQfYK_fL&$Ga|Z$ z$;&!*aC$DPrRT;s69@M4_4b=SbixeqIQBq4j@w55sGgl{8(_i#1P!~fLdw&F;DqDU zjAM{a9*Q3bKKHSB8@=$WZa_c)9X|p>0(j3Mi@!zN5--;Y(N`Q6|CLwvqO>r|*N2I9uB${x0^fxc!*54}%a3L%5hMS)C4PIo9^ z0qJ4X!N)s*0+Yh(g#?>|OhLpR)aMj~7{bk(JGFHXwL*^!cW0Am=x(&}K;dH9iv>OO z=9>={9{PBrMsMPMOB2)66PFUuFFvr~fnA(ioSXv^TbMpz@xXNK4RVfkp~^ag3_=g* zyo3A^gMzkJ;2pieWYC-Pz%T}raUtrLr@Xe-|7|a z9ZtMM9L{;i9dzOyU)UkRkqsgv0t3(kKGvJD^G@ic$ejn+h)vtg>uW#u!HL!3>wKRC z?-Reftw1?=h|OHNc=aFuEMN2ht>Yy*T~0Ni;3H03?YT9KIhAWJ9cG*xR zW2;NVY%W=RdeNfOiE8k6oLBjAQW&Y(c5N}CZb^pYQkF(tbG$IP${2&DPKoh{yt7 zx)SIB(WSv|-^|;cQXv%&Z$9ekh3er=dJ=6_AQeTH;To8#sHDCT9UUDhdr|%DHYkcL zfXRf&2*F>=7boVzB6l$P+Y5{p3Ey5&3vSH4f7Xnd_s_at_vGZ$%wN5Fdh#i1fXr-@ z=WX3OZ}ObypQnAFDhn*n_$efU57oh^6n{WQWT*v6xDH3PZK1BeyB$xoOD@Dtko(H* z+JbH*{UL(guPPSaoYO8jxn2CgfvDYLD>y128`w5(;DEO6`V$Q@VIKa>4E>;~vtCD) zR9EV4ExL|Kqa%{mBir$s(-S2z6~CXuduVI#=iq%Cy5_*Gg@~9;?Sw_w!oq;m)cukE zXtziuMk#_m*3pzE5U)8VSc~-Zlqc*NvTg6M2_NsZw-UEcm@${b zyAh6scOXN(dEPk=00P5(1+Yr@TkbvGy#?rOie{h7`seXIlp!)3#pn(l)n(hUh13REIXggb6 zD}TC)r^|C-3k#xijl2+86lF03^Xbyyx6EPv?`a3?qh9Th2!!TQa6iW!2r^h#kE%8J zFoc#7W1?t9ctB7*o!?-5L^r3}I6RTrQ9Md&i;lJpD3=$H1|=|El=|D;#WUuu?8cVF zt;^Pl1Lt-TAKmolx))S+1FQAy;_***8Pil<&}KuHEN#i|I%xSXk8ZuXg!AIJ5pci`uxPDAJ1IzYhg&=Sq(o9ZZx4s^0db91U4YmgPmQ$8W}I^o`-a7FZ2~U z+Y-9A3PsCAn3BwZJP!u)*JYw)4jdY(R5?dXgD*-Jkqy(QS3-w&ZJRbGOU=tHAe+rd zrZbvjQAYSzssB%x8ar_&a^qI-VLkb3vjts5Vnvib?N@aZjT!RD38;T`JK z?GTo54|qhkY&W{5G;0v?SHreQZ851Y z4@5VD3BMg-y*~^4FN@uyss*pr%R9MXBNMjHXfi#8=z|Fx)m8AN&^e>Mtu4ivnN6s< zfSex4ajLtCUu5^-2eNyJswe=JOpgGo4hA3+p5u(XUOWOXlu!P7U+Q1DSNDCv zVet>U_kF7Rne?0V(@E7{IDJszG%%u7{YX!;0X{QZ6f7P%Kru9KMEMNl8kKm6t7c-- z49wxF(|fB23)iTJ48mU@8RGx`$QX|`6g_X>Assf17HSEIBJ@L)9Ih*%3NQ?+vP0~M z5^i1bK`q;hR-M!)Dk?(8fkQ|2ykg=B<)}~KM>nl{qSin$=K7^ENx{KGLl4`p#ZHV5 z51r8X#MG;W!3B!xJLLnF`TBa@R20NiFhnf~%JW!{fij{X?Wtxskc12(kdhyJ>v>{m!{oMiN`1Jz4%TmZCnV?t(%V3h|F6iLZz+*#`qD z7`LBv1$1H&s7?;@h$EqbEm5dvFS9yOw$!A~^{a$m(W8z&QVTPRY0!+L;HZ{SWDo~g zK^RS18cBrCx474Q|Ki2)S6?HUe4MudBou4iPrBUF20;~c+O=*#A9tUR|KzXeI!k~^2DQ) z^E#V-LfaVnB=>0>y7ckxkIu9A-Pm>fON&R%w$@WZ7If~dt^Yyks@%soSnxxYr+Heu zso9r0%=JUW0_$7bhTnh;?KiBRY~DI}G=&PQaT{?Im01+i5K<~T6bbq2uM4`pEDDe8KfHGB!Pmiq{js+g7?+%iilnbcqMFd(gGfZG}wH?Fe0q_Zf|HZR{&NWn=qOItH!9v zfT$>6#e}*toN6K?&@GJ%Ftn6`RgM7U$)I<}hobDK{heR$|Q)Ck$smoz>i3vZdx0w0Nk}nt9+pnILbYI}b zu;0`H?9$SSSq}rTVZUEt(f9rMVy`8ed$tc)zE#Z4et136G85VWlDCZ;-~~vtnS7v? zLAw0VM?_l}@X2Yw;Yc(aB(j8`N|J_B&8uN_R;i73Ak#(%KKv~LHBD#sK@FVH94YbE{F?=4KNbKIl;#($-wRImPg$7$d7fu3z{}|;&QJ#>*u|E z&?vavHYklQH$fZrT6j+nIM>*Zl=I4j&VTzOZrK{zI$Q zzNq1y&N>R{IzZ?}XC2UlM$bWj*`1zYKxm{KW-~c6sIoh~*V z)X1mB7PEAqE0stw1Xa4A;i+K`f$H`i;t0LH0{pycdDn`J1fxT4qCk)m6B)y;75AY2 zg*T4vXu(WY{R4B_CMUOTmy~1>3f_HwGdqS@Hu>%T?b;3)(6;SB;v3q(Ancz-7-&Na zJ!n>#2@Xi<9vOSc7*IwMei%vk5d2Xbqy1j;y;K`KoMZ<2`CD~PvCefe`sB{L>5CiA zy6PkCxu2%4!EA(CT+c|*O%k2iqlBnP|49GnD67$g z?x(JEbZ{vmbWvlVb>3xD_=c>Bn?fx0pPRb&l^f&x4omxW?0^ATWNN%JWWuD>)Jd_*17&R()uzi`hGI`vzQT8=XoHM`%*M}`ka}at9L6g}Ln1^zGn%No`t38)nj-+&%WEw6?G`)xRhC&H{T>J0TKbhsb@2EBOcQY0( zoH=XZA_(T_->$GI_1u-;)U$_=t=(|!*oL*ofLHM2d@)Z>01RYfAR@!fyl`naP`N|x zjfTY}NvX6aWlaPP1uhf8>TV*;|HBye@Def+7OxC5$W9YM%+uxVhlTK`YQL>sUf9+C zg_O7PAejhn5NyeSEzkj5Yg-EtzEdZ2fN)TNjptNtqlpYth5qh(mUsIp*0XV?^z02| z{k3x)de-Y2*Ru=mo3>EQ6Px#*!-DcPMY~@WNYAc+3woB*t$5qurQP#`ZV}-`2L>Em zaGwQv$;FqGZgGze6UxDB&W6N#a-o4huNv#F9M;5Er+14sqZSiYzC3b_zwd_u3)Ahp zXm6O12pj1T)*ngdE`K^Gaidz0NLFIy+DU=IzGxiPC{i}zK87ELV2Gj_tycOtCZ2RS zzmbX3)S>kJcb4HPJ+jun#_Im__FI<&u7>X#l-)V;74?Vj54>?w`OO;J-_|are!~u5 zZQQqQwco04@r^qAM&0}4y6se+4{)`T&>_h1C#V(~1h~ngYzo|=EGJbVlkS#fuDeAl zI|`c;4+RUsR;zylf0urCnHGAbK^)iBv4P^8_iZf$BfJ{c>Kwaf-`ce|prP%TJGWBA zg9^%$KDbf5=ir}zYB0=`{*(%F=5X*R3^ka+Tq1*0(*Y7`5H}vhJsR2Sq;%me^8oJ< zxqt&=0N=G}5mPN(tc2ry6YDSgsE?^v)yJ6do8_{BbdOM2M9^WpW)Q4@VLTt&Q!hP4 z9QM>9@{ktqq#WwfJ#SQqii=RY}g2fknb4F$H>S;s^J;3f)nZ1b3QXj_SMI1k{mk2*rn!{|65#9|H z(cqe-ghj*zwA?caNJWsHR!~sePK=|9dMz>UdlG}&CQS(qRW|`;#^0E%PHOnFI4!_< zBqXeVcR8f+56P!s-n7TDCfE+`TG{Dt^Rmx;iHc(od4K=q@NJg*_bt9iRv5b7wSp%l~2F< z>Z`B6{_50O!s!&)%8wdOXf%iF0w5joP+*0{n+Bo_lr+Vvi;Ir6vKNi&$E@qmK+pN( zKb|aTgK_Q$S+v1gLIg-pq&~R!U4@Q?o~67|nRi_s#=EYf=Wv4vzZfDrv<7pQ$Hf&c zfLvr)8z?{qpqF3qN__J>y4! zAxF9{j{po!sSjne=U%0b&;Zb|*0YpEDMgy`BTA`GrA=8=&6b9nGM0PZAltM~rP^r} znac>DAYjpl5OFFv3Z|{OutX}9R!_`c^PjwF(=s2PIVSC4>8iNs^zx4i{xf;w(@TDk zu8(|va_^Ke{rjcAF|uFJk;D5;-a6vH8?zs<8vUP`HtE?>q>s{p&!yOxGJQ#D)0UTL zPRhxzOkYwBt++7`M5Iz&JoP0_7dx}~ojb3hRpb5g`J+OaF3NQ~yL*G{&qG^FV>Pt$!$Z0S92(YTDfVGYIQAE|G(=rHm1S@VugAsmedyeWo@@OvSK$=1z^eyyNg9718@ zFD)b=W_k95b3R+P{Qu^x$eg=t#pR7tH*CzAvUXMO*m?8Dj#`i>T_5q}xXir)hM>nY zMsG|>*)%HS@gReBPtLffhQF4Xl$4W`G+?sWWL)pQW5@RGn@0PciS-S4-*3crW51yX zL@5t+h%HvA8m%Lo%4y@mW~pGmTcf<4uRxwA2a9vfP1n#-!LXgU_Ks=J#N-v%HmN_~ z{_j5lH~r=eS+&X7^kD9+w{w{H`Rui;v$OxBlh+z9&Qx!oQ$JH5`|OE+iRX_bkD0J{ z`kTMMgZ*CgK+3p9wBP&1^Ws+HY}~Ik)6O+!Y&j3sOrawm9AfxYi;~2r*?`o2C{#*2 zkBT?3&SKS(!u}QewJZvEibnm%C3+NSNIH^3p|0quXRiX$M+Mb30oi{K|zOayM42>y^b zUb(^+B&6ce3Ds>bl-Nb&leNK(8|*jn!b$nBlmCp@=Gr9>l3VNdK~Gmb$Acf%U;^-n z9eD5!>NYm|bC#;UAP-`FK7nEN#wRLH9<-KSx~JsUN)|Y{!rVU6z=+S5TNlnzvOf8Q zo<(1A`7=30-xkkSGiQ_B7-XT~>zI^MxJycrKRa=PZ9Z{=kK0dOB^?%Dx>J96qPsu5 ze+hqhA{CbC<>n8^+n)X5#v1mAtE;Nv4_8;qeWX8~{_rSx4lDGB(+9{O-otfB#5!I6 z@ct$I;dlq+$o=8HT<2YUq>Mk@c&GkwbxVc*aO~|3>}^H~{oVcH#yj_it6M7a zhpVelJ?5oK{NWmS+#k*{TFM{phSBQ#!-3d}{Nd^r@ol+vS${Yo*X0lI=k5>hN$$K} zBtbp-!=3Mz_JyX-NN$Z z=Fk(|7fxUl`@;K?KOJ_v?U8H|;3(+}=Undcg*(83 z4rl4WLvKxyyZORFf5q>W@`aPkthz5;+)|z|9MA`0?@If^2~zTfWB(WfYTytMdzJEq zbD}JMuY@n$DbmaHg>%VW+82(!%KC1 zMF|CumE#NV>F|a3D(wq5-mNd3sG%}nI6lJwniUpS{#%@+<0;>M$u`@(yab%og%kOB z@P(^eq#rSt2VZzk?h6Ns0KdZ*4l1qC7cOq`=nIFfMEEJ|3)dtitRXjFxYGiviZ9%5 z_2>)7nqK8=^5_f4wm5y^8X_IOaAHBvzHoI*Nng0SLS7+JeaB$Iqc6OCiClGGIFef{ z^o2vlJSt^tG*`wKu5B8KE>O}GYYAUC&{OOS2U)biTB_m;?^VJV4zjDBFI?SHo-bTo zCyxLOcj61@#}f~}a85-v>kC)cRmB$$d@jYlRLK_(@+;#D2U>$sy{tT6_>$7DaLAtp zz+VF9thO(lBd?4voTKh8ec|f5ihSXKH$_@pU0*n$Eyp*iiHsWag{$kT;0p&6UFZB4 z>B$!kGut8c!%6Cw@rC1QH(xmLF$VNs4PQ8LQLZoCLHhraFI-($eP1}%H{5-{tL6)b zKcs^Fu0db8w73esaQi}Ws~oKP!s%Q?Wi9x@s&DzeaO`}11-|f)Jh{Tb&k2~TA?D(~ zaBVJr2p5p~P_8c=b46F^3;)QoD;)EEYR?l7%5|VidTYLLI`nhr6c*@Im3^tdW)La%*ug^mNsI z;W(@zNZ*q$9F>Yn`@)5ea%*O)%ok1?*x?IjCYLX~C>I?DPgma;j?sck`oh<_T;Vt` z4lBAW*OLd9Z=0ZKbeYp4FGo!`^d9c%@4cw1OuBG^Nvi!q?y_aMxyzT=Voi~wVPXx` zGwNT6Qu^}9;X`j6!vBZC6igTiXMc)pE5|m`(xtdjkQ$8Hknw$HbE`W*b?{j6z}T1r=QFA3`?Dnk~01LwZligfB2~nM9U7A z-aD&fpCKdqKl9G9wNu}i^Q7@DfPl566D~n?GIA*Pqe?c(L=QFz&>3IK zCP@+XEaHhLzLkyYj<@%VC+zJGz5Vtf_KDga*`mN(4)FFJ=B!V*-9fmYr;OIB+|P5Q z)eZ^71-%1hNH;K)Mj1~%D!!m6e2MiA{ULuZ&rTk@XmNV{q`Id%AnxbaC|%_80b9Sz z_dT?}`=BX9hE8eTppn@5E%p26?caNH``3UK_yMecpw3Y!a#E^sHxEeb)Mi3NSnURN z8o2J}Ls_V{!MK(!dpA#jx~`(>YCd+}nFpEp<10T|fA*TxXW>#~^zL!nPW^p+%%Z%J zqn1kY{k~aOk1-?beE8vxUEkT>cjyO?Jp0wY+~k2%a{DLcYBbs%e6pVNn@3w@qMI#( zM8R0cz5L^%&i;bb&t4$SGro1Wkmujy+7Y-Y0KHS3LM+|O2UL154?Hw#NSeAuvxc#a zVzpQWA0+7_N`dbiT~Isj4NdeQ*#Cl$xoQ zX=ulUAwv>6;@Vaij&m0tRXyUEc5cc?N+eutFcG8{~RoKGz4x>H=pv>I{m?HiPz0klGS$t-Nt)T@GAc_l71X!F?Ilv=aLOH0r ziq!+g%fj!cv9=H)-qxnPI4-Kti@P=+&4mKJ7~L7n1*ul@09%QYP4U7h(s;bI(tyqY9>2rf^-0%ONoTl_ zh=>}FL{EyN1PZIIIMmJMXb$KN!25@&a=npg>r-h=7cv1;U7=lQt6jeK;C=gQLVKv2 zD^wpSgasa2&v_D^wX~Qn#BvGZ3T$_YfE$f15!wY6;yli}R0sxfDG}%p;H3ckpy?2F z{j5NT&t(j{2mEyupkf8EMRmGs?12el{<5nc#nmRJT7 zq-4tEf}L6$DGe0_f+291dWMkOUSdh9wvYbiGl=YKbF1LJu; z?J%C;8m|;@1^e?J3v_%>KrXWw6YD1m(`TPgNN+!P%=urxJf!a4(D$r;!JBN_EcHBJ z_b>_PJcD-58>m%z55ScmBH2aMbJ55L{VsV>MEN?u+?F`iQ2Wy5^3^_cK4P|040IMu z*zZ*NrE4yt^kexo7t6B+5FY9}Dq_Eoza*UoK0<`iuWP1&h7_uGLmZe##ZwS{IgYSL z*@?O2N(pzp>x%m>WiO=Kyzs=Gnhdhw(PY4^Y49H21iX!e2Vd7r1`XaO6akFL*d=n zpuay1+L?C&bQp!BYZM3_=7>r!jz$NRx>91LiR)u!gV#$NIu$Fih#ER6OV=y2bs!_w zb6M2$bL0UOu$fR-%AXOt6=Lhg%L>o8z*Vs9sqE zry;~;G|O;V$3iXzlxWq#$zrO&uqyso#bj1fwvwTe!#eC4d8hQbep7XAC3nBGo2?}8 zbTgJ91u?$vu=F`fvsMy=?Pf123tje-&MY0$Q*51{>W$GR1j4nT8BG+`#(nXbWH^Mn z{_Yn1t{GDZuR{fq(rsN!*;7i6yFDe%5wi^kDzc~i%d@AHTBQvt^v%V1f@?gF_7qPI z@MKRZwMrXQ@RLY8U{6s`mS6!Vp)#gaj^xp8&Z^@ek#>}CDbc=;l#aqyBKHS!DxJm> zrMB_A7_C5ddFGIG)M*aE7Q!#5v6c|6jt@qnv>K0AP!7-3yi@Z=I_lZH!E9;L9@sa& zuy2;R?3)M>TeC723)Q4@vukoFGMXQirV$ZV*&fQ#A)N4kW83Vh%(jt^!nQ&6SlBl3 zOcmQU9HdhvS@SOnP-(k-BLJwP4H+#ipf|PHl{418no^mV?XfPoKu>N5P;C>T8 zTgq=zp49;ktl%VEV@AZD3XKRZf4hUf)^k3MwndcinG{SgWYCR|nu z5+%UWdD7G8Q*B`u3+Zu7v?V$!qE2Y7;6Q)bESr&)PIn5_%$kbf)4c+U@@lU5a<5g{ zG=B5&hjx!E`&@DI_cXx}?Oj6%c*saO zex=4LcK~!0a!z0QvtBT&CK3p88j3Z@Q+XSY243hRE(nbqHi(W4uUi{2Sbn}fmN>6C zt)n=}jzAwW;2da|umz}vT*a#Z=D1A1#&<+_v$7jx(23?tDMUrT+d{<`$}*?j2@cu~ zg4D|8)SE%nD+{&I7tRZXi&3)>!=fNip57#)MAQmUFiL{xK$2cYl)_hZL+PhZ8EZie zcAYXXI9Mab;M&2VmLLmmO0`nsZUpJ=nIKD?>r0ZM)X(jnx+MMCODR!VNQ2651vxAs z%Ew>m$olhjdB-!e1y#8Wus5>@PQ@jg;KDY z0?OAIEoQ3g@6Qa9Xvp-Vi&R`j(N9SpYehK_Ngn7R#sO~Qft*cwf@m_BL_@jb3MLxX zRPL}gT>ATX`3PQlm5dA<#mn2L7~o0=O% z>SOD3PrKMwEt)rN9F-8AP`@rU2k`T?pa#4gsf%>&IuwVo#14D7eXH_S7vFxhu%aLj z-I>awDYytI$2KjrRcQLbZiP*E#v%7mor_oELIn~!H4D^<7<)@4+OVhma9pfNsTg+& zB|6z%RhAG-aGeR=i>@1&IOd@!j84KYHZuf?5aur$jHCkT*p8Ew$s)=|=yo5dFN5BQ za{IVY=9zdC5>a>)En&TI+;^uk97bex{FEVJ&{WN+n5y)wtzDHKGjqbBiFIjSr~)_@ zN3v8~)3`Q`8hUX%mL;!bEC^SYo_lNVr2#Kr6uB@YT>{6nQ-wP)_v&N0>cSr;E}`KZ90D6@A>q_l6#l)3BKiGo*24d*iSK z$rLF;z<07WCG11pv8wKi!!oJnzK{%m(ILY-A#Qdki?;c?q@yRC2MzOk*MIz@N7dy!q;d-&L@$nU?ZMik`=@apj{ zVmnQ*b9=8DvI2()Y3vc3IR=GUy2OcI-Xv#(K_0yzS`{=pp&LZ5?a?B~2in~msh>EJwd%w+a{<0Tmfl*Yp76*1&ZEOtmui{5=)OK$YvrGGauAk{Xyw4)3?l^s4gQZ zC@@on!Arww8&v(#_{#NqV;XwByNGo(MF2Bq%L zDw1eLMrNU{I)n;Wl1{i%blT9`&=@&%$n>O&Ny2%pCuc(#oswYSbys0<>$9){Vj7i8 zhkJz7iiyIs?&Ef*1(Z^?&m-;;b}4xvs`hyr9~!7MD6$NGAb4PyC61ZpX^Jm?2)4cdCA}8 z8)=7@KltY8v2U(;;NaMT{Pcs%A3QL6>;d|143|gJ*5nBp0~05WA6!t7q`ex%^0H^V zGjqnfGqR_@J9Fl{I(;@?)35lN`ELFgvKQEu>>;aHrDUh9S|umYcm5JK)s>IyAjxphW|flMZv0$Gqx=c7Q%nUsa=V*a`=!z{rf?UB(#rj8QU@zdxb5F@-d?WkhV8LXsAK$5FLBg0KGJ-x^udU_VMPV zca~1t(z?yoiOY^onsj(c=CiS}TP7_%I;r5c`<{;Dd#dt&Mq%sJ|sJ3*|L=EA&yt>`>m(_ z_UeZHZY10bJ3Ubtf!cwyh1=TpSiB9QS%|DFO6ZY=s{GDhsb!X9*INp{aQ67>QNmlq zfgMfG1ap>veq_AGa!#T*nxB~@oEFiqLz1R)?-dbK8;{Q!4A^^m7@Y6vS(KFbG+oKZ z=*&0KIWf;S#Ui8jn&(xBco}QM0 z|HY+~9Ipx#qMNkzv1lu^I7m2k)jS`|@`k^DCNnCD5{I*_3yOul)Gs16X)aBtUFX&CeL_8=fdNgst zAC**)fG!P15OqmSn+ig!mQ7nUML*ca4eQmZ6@)%GbUR^1vqEHaqvu^DNzy75$wi{M zk%ddr(FtmYiQlvRzp(?#lNHv@y&b`s(*7)K^_VQ&yh(Ld)SoM;A5gfF1gvfg)rFIU z{zZS9sSa}pBx-BvJ=`VgEflFjJ<(8g9{zm=v0ZS?xb;aeGc>7_4MLvLThv=j7HUlA z3R3Z6f<*?C;;)uefeaMEWHBW{pqepc$MS}v&hd^Kjm0DA`3U+kCL`JrTJ!@j2&vc_ zhRdfW;3B7G4a4PE6L66qt%l(u@p#w!7+UjiQ77=bhAX_L;krwZV8S{7vqgu7QgP0v zO&T_c2oI|tS~nC8H%m4r@uA*P5#<$Q!!3uAp=MN>lGNpPEzx`m%9kZ7R(on?QW(I^ zHV?I|WyqK-=J5_0gKDJGKbm;ySB`i=J97Bu^#(VgVu8s5bdo$bpD5FA zNt00l-ZUF%|l(27%mOOsn zV_xXP6tmu}=wURWEvLvbaVZUp#zf-F!29=R3V7QLhm={L=F9YEy;FGgt87R_%~fYi zl41W6xFpJI8eEcS{}Q+)*lHSFl5hVKxFqIk8eEcg{}Q+)^lBPhl70UQxHSu@yCiIe zGhoy3Fedcu(XC5DhkM$`$F=O;s<(D7EZN@A%4H!EnXA~|k6NcYrK77Dy;i=B`gK>< z{EzwaGotR}a(mNNr5JL-Br@>=Je2L_XX`7b?Jn6lNP5EN=WLm1lDl?kB^mW}txw0w zuEi46B$yyUacgEY7!??N4)KNyG&5SJlE_2@d80`W%TF=zKIeus!C-WmdmRL+)DSKa zS*0;ZLe>yCBoXfx9Fl-F1P)2Oy9I|NTn&Lk676omAqiGP;E=?+TX0B1)d)Cu2Z_4` ziq_;$^1vS5Yzc9(o+OWVH6;(f0;q!Hs!|#B3l~B9hzgxqRqIl!2_mtQhEYt6Q9hCT z88*YqRi8(i1qg6cEvJD0S-A6|hzJRnUlf7K{KN6_U)jCtape;fS&7B_qHv$`guDiE zl9bEQnvW18Qo6n{6hDy5gwP~ZWoDjIUujS{^3ZDt1GixDtf9KjKKL4oRzFdmKx^g^ z^o23lF69&H(<;G;;rzw#&}`@4UtBOU;U8s>INh)UFgD;2)}}#A*L&bgHE2t%4foh8p`ky|&4}frG{n_A21>;bq#EcDNe9d&d8%N+u}2*x9mO5O z-Cm1AkI9pJ^v%c+nd61>$D|&8GBWz~nB@G)=Qo^^3r(&0{HXU>wYi;iT%~ypr=0US ze`0mQDdm8?+AtMnc?@559V}Msxx$v?8b}a`Ob}1v=SNBTe5Ga<9tT|E8VN--7ZK6LHh%QlvgBzKyWZ1_{deEeo}+s8i;M5298|Wy z-?iJY&R#DZVU5~0q7omNdxPPGywEfdbE5`GRpw=$=N#c_PQwY0bAG9BE}wFd_(FsGGi8v>neYDlhb zTIdpeX8g7>W44VS|IFwU|LH$rLjM678OpBI$48HTivC~o?iMz=TaT$zdvwbt`M1{q zHZXn0`4TzsRDhvu4tzFc5%DZ=N53*WS)%SuxjFE~Lc_gG5j!()_wIRfUw(ORhk*k- zbR0O)u>7U_=f3pP-1}d8Jo%n`lauegXAsbP8oaxvB<}+2)FTgXqN6$&@g;c|k}r@& zvN-m`r=Kt%b<02MR>M?kTj`??V>>wSO2WI!e%)YWL(rZNBCj30Kt0kWF%|9GL5&Dx zr`vxiji>4*2?7O|QzD0kJ*&5_4nL{|$MaSws!&qjVts!3{8!d{l?EHPW?23SeRFkjwPm!rxL8Ju z`KOdW)FuCdolvl%Ds}?PSC!4`G($-BbOc36ICX@5?%(X%M<;&2H8N{bsv%_O>C-#= z&X`U#lz&A@R!8f<#Mlm^12Hy&2zJLuyr7134hCZyV`~lKg7dFj zJ8|O5EjD~ob}Cbncbqx1BXRo7zIaMtg>{uT?SC462M@R8JRA)PPWFZb{*}bcV(!v( zrZvD?tm`5n$79+yh>UL?qz_W`_J}KntYM7JO*MZ18)HicC~uz7wP@D1`Sa`MrjNGp z`SMRH2h^1%Wh9@E2(uFV#=f9@`zHSe#(l)lwb*WgJcK9$!9x~NM2luO8T@!LBaPehbspbxivB3$)r?3B&1rzjNoTDrzAW)KU%>$u(y)6{cYW#q73~p+@RItwCAtrM4>mf zjLRpZU=$tZgA{;EeKH!JsM}#|m)*9G`ePAKzw`P%lc!#M9s4xJ{;GXmjp~QOtyRv__AlUTa`6w?hO0nC4hRU)b7{de(t=3=P=iieQB#Ak7d2BOrm*1^{{c%boih%~ zP0yA4Su$roxa&as&bC&1$bw((AF11lDBGmk=K}jC7vI=FDs?W%SM5b=ZqbtJ$S++9 zd(=Mf*42-XU%xeCQs&5oyS_QQqyLQAkSjs?)GbHdk$N$)1s)_`Oam{ z7MqFgXUhW70kzny4EU~P2x7qq`$;uinEx;G;nA{uXg^sVALh4E+ZA;yq8@!!JuB&P zR(dP{x8tfxdg=@N-|FF_c{O#mEO^3`oV5S#;hZN67tyDpm7yN!ECeyKO@wtelaVQU zNPM0&L&4lJ7F>TZ8GT=I+vUZK$frTr6jHOuFN@%9>Rc|OU@8g^c=#lVBLvt{ehZ?! z5nSo`bgC_?VZ+*?UeId|n>1|FxKTt{XiV*x06$BJR|vo{Tm7xx(8sY>BvDwy@jcSY zB9LL&hAJ*2Sdv;JJ0e)|TM#O=242J=evUs2QLhxP^w$shPAy=%a~G()^!c3J+-bSF zx$0pl+aToo>8>GM>WBO~(of8MYR)NUR&Sk}^QHRd*Xw7`nYCuktU0sS+g~_!igd0d zyj1jqWxSydKRw(luh2Z+_aH@B1Ow&4k%SOpgu3H!i{k?YH0WfbF4K(AGn^NxF|uMX z@YphPbSvD^Z314~ml=VcPc;H#yogPO1CW>d?m+OqTM)>zeRm*G2=85CfxOvw2SUKz zf)I4qAP|q;6&A>ees@@)Y^%Ehq3&ISQ15O*h`DPJ8r~fUce4j>9)JGQR^RH&g!b_i z8P+H!ynfv}!GXTDt+nA6@?ky}?kIJ433)Sje@k?DgQ&;|8Fd!@wj>0d>fz^h3Qkmf6Sel^dSxhb zH<25piBCm81`<|3piUjaN}cFB(O4d$kBAZ zFS580X#`td_`cLpU1iU@VGs(NOJ5XDDA+7*RDZQEg-zT8<4=LFP7ie$EJUK}ArhK# zxYQa>G=bY0Jrm_T?p0l3VtOOfF+HU(0oSth|ET*8_^7HZ?ss3AmO3*jgw)AQ8a;$0 zbOu;@$AXB8h!DYspd#W*+!e$Xv6r<_Y!qpN2nZ;kf`Whr6@gW7v9pLuOET}~`=9rw zC7Br#9l!7Q!H-NxUb*+&bI(2Xe<}o=I+WDD$b_T0UEQm3WT8UKgsSVnU8t}hh583+ ztbiB@`X@I_YZe!qo!QKp=CoU5Q{z&R`;2;1Lwd_1#vJh3P$0x<4+;(h^B|kgH;TvD z1o0T(xY={`a`6WKNusH~9}vN3F$)+0lUY?mk071B3u})X4`^pEW$>Va7}?plUV$zG z*BFcfra`+94uJqrbw^G?9);!e^YfiX_>@_qC_8+dpCj8?2QheO*z$r#Z#-~{kLBmU z>|;-zVvEtM!ORw);`WJU|X7=MBv9c+3C&@cP}lKnK|vz1m=pUs$IXjy%L&D1Ehj?bx`F{74~>oJH{O@AkLM3{G) zZ#eVBj2V{e&psadomXS+C;Qe1=wq0s(IB#cSoHx|@H(*9Rt@S`DbQBFoGI_UF5Xi0 zl_6Js=ymDkytU#j$f29E&ZEUj@!YuOf9$yy1Yno~zM8qkSAGAhN-+s7u!gp?m!*8WO~ca3g3Bl9W2i3-ATuW6*`otUh39 zIb%lEXG$wRTcZw}NlVS{tVf?HybttZGtWM5xqilsGf$Xr2>Cv77&~nmjWf+8Xng>Q zo#9my2f!d?064F=%fXIFn6z4DLo;dtl5cLz zwYpjwsqG07(FRkoA=Wo;S{54jKyBQJ9o6N~dgCsj<&aFp)it6tvgXwd@}&UVsjgP# zQ5$QP(i@nFXs2nk_Iz#J@JqorDh{M^V^`Lbeco|H^civFOU4&A)VwN<6sv1$MMLby znq{hDAZpNkDYd_Y|KWb7eoLSS301^pL|GUZX8}Z21HqaSBv@IwnSrI4{Gw-?^x*62 z64SftlGmjNJ=0znN1y8|j*gQalpb8GE)hqs^-QCmj#8KT-)n0TwfDyTE%4sowuFDL zNN0$)-{TOC>gX=DC&r;n3t)?}F~C7#17Y||nn0ntp$bq}5YdSQ03CF84znet`1cVh zE+8=NBid0$jk~J~+056qlZMZRvzB($nL(z{XUsU)Vn*ItHj~d@OD7NHskXsp;15<3 zV*)Mmum({cQ~Ye~xYJ7mV~V*<8`G&hahJ}R@!RsSV`A(z@Y`PG-kF3h`PkRH;O+u#Z;N7WIH&(_tRHtpB;UGVcXXECsQ zaf$bN%*s;DAgWc_A&z>(bBkx%S`qS~>WDb{buW_{0JF>1sSlg>nmd4IhIK}PwMRQu z+~ge#OI6%nZUZ;^I8c39ng<>=eij(q`IdOBZy&@wj#yiB!rz24gjawQ#KFz!7{&jv zOp~Ak5u6)7prQ)WuzZ*~fn&5i1;yJi$RSdM;K7ugzMZj7U80Fug2O>_R+9W`dA)L3TWc>^|3Ma^fAn3x$n7*7bR;56WSwjb4vxAynU_XU zcwZ=;tik(IglSll2b-!R=-hlk9RZqyPeRw%5rk3qLbx$8mM82pePNglZ-HT*BH;|CwNAy)-(l5q+nqHr z(wtp9PHwrYCXx1^C<*(FD-BcOO){*L>CDc9>Oi-U0wM0+DaNV4?God~_+9F6V%$#k zw|%?B7%_IIdRqIUo-D@>I*!js)L$P-P2A-@a3CEdY<`L%-CWu{*n*5TNPULl%Y z=O&w;ti?1H?U~GmYnFPifrP@mt&G{|5{pnnm*DQe7!|?k&Y^x=7$yb~+%^r^rT`sF zl(dnXC2i=Q4blKtOHE?{AA@Rx`(Fj*(&vxdgC5RaAV8|bs7map{u)-Zmlp_D-6y-IGKP0fD1?C z1FDOnsV*j?AjHSQr)eXFlV)S|&c^Prw=XO0kdM?=WP!Pr!OGz3VrH=NgO5|F7XQq` zIYAmDzFcFtY3Q(%nE!+S=Ov6d@V)}lB=JTKfj|pY*3Dr|PGUlKW>P_Nfj$&Nn4w7RU1hd{4x;8# zNADtoEhX~s4!^{*58cUTe#yUi=|%p{mv^z5hgf#lK{LzDgDm?+-1OzlnTNdh%p_ig zx^6~_D^@er9U{*iT#Iwp3XmIUfLiN9@JiqwqtL=lIe3&u0ZR(&PnHyemJ0rbIZH+b z9xcH)?R^lP;&@mUoWs@HBY~ex{O5aS0;(tjG_BBSItm}K&$QI%VK%tuo2Mpb+p^6D z1S;Z*rWE0~BB9EG4y{&XYcK~m$qH6-fS0UEDPsTUUV7;{rINl@KdxL>mX%djy%b;l zKg6%lf2S<)D`XG{#*`*Fg>Hzw=uy)O@_r+?y#f*qV;_YNj7|ds*GA{aN^hQ$oS0@y z(?@6uJ3COLcqZ8*wmAuG#(VbNIY4^_Iv^J-a^{4mR)*Mp zF<#(c(s*TISO%a000{txZz|HpB8}HKO1nl|8hM@Zx_#rkm}rQB2ai`8C0(M?{Nk)K zmcfsgWzoKUkZ+5LGrPechIL5pAQ=@CSN(qVMy0{_H2TIP?f10vxTGV#aaE&}gQtZDWe{NE;CpF&c0^Lr!N)j_&}Fz;3NWPOfx!wV zgkYjc+RX;L*(Ux+d;9RE!9y1NFV)7@|J+B+&A0LG#sb!ZZ^w7TStq`WZ)ZKcpWb;^ zZ=)HhYotZ`C;qR^pTEs|YIk}+*#_2$_2h4#{~DioB1E@vx;Dx3q3HxhOrs_IGOAxL z5z2v?^niPA%NF^0z#K0^@k&f?76i2;8!jWDCLts=Sg=@G470>kko1FmpJZZ&L_jYZ zMT4;-4$!#ZE=o&~(Rhg>KFZj53MCEe*s(*0cBprxAX~>S9lLby)S>O$>9dYXP#^ZAF>tHi67X5!V=t3DLF z`tQYaK7U~R_y_2p+JY{{lF%186sA3r(ON?ahyBk6NafT47%mKsQNGGLmkhuzb;0Ugnas8GQxLu?TNnca~q!2DwFM zMqlGuN~*G`#xrxo$UFaz{m}F8bLZa8-1xk6p8HUTi*o3UrJi*UM|C5M?p%E~r1mbFkV z(ju0_a->D7g|!gl)vwU;$0gn2nX4Z0%!NFJ?&W``U1;rYk${ZRfp%Lg0EKbRi{uXk zhmXRCAOaK_f@PR3#bzfjoA*_sBIP+yG6lss#5*yH(f6}){L=eo^NZQI`|FQEDym}t zu4T{j@wL2y==VE>-7}^|P&D9|#A`KAL!jEjAQ%uj!9?wtCX}7ubx4av{(ML zrnRR;LYV;ZVc(61N8QR~Qh#)`b-WiZFa};wKyPs{ z|9d90tHdwaRcdMAg&)#e#Xa<1fOGM7-tor)XEIqpH)EiU$#^&3h(J1CEg_Ww9)0R6 zVKy0%J7+Q)O`{NVrBhJRS4_s%7k|)ABa@uUf$7i9Lxx>f0ZRJ;gn0Y1LEW4E4VEf24Ys-nE~Y z7ns`MBK}I|S4!fW6P+Vnj2-;T0*d$tcbi zETgq=ENH}>B;v3danZ>B{knB!!u69UT{mI;*efr)bmSEmT>;#~5&cK>a(C_9t#5f* z`*tk~3-Sm=*Xtkc!n!ombZ%Y9Y5viGyh9{AQ1KM6yLD0Z;>ES>hPoeoi-d#nQYSf8 zQ%LjakYC_T#<^2c+J@tO*ljfv8-2Uxx;)$?u2<&3dn^NC-xcETEJy%Y* zW*O4{-V;Fw$Uwp320cl$ZR2hU69t+Md~e{J5Zzg7UZDH)hcv^!P`XU#*sqv$`22Y~de~^Prv}2gTcE87qY)gYZOv1Cf}Z?#hCY>%gtquB1Xt6-g*ovf2@E5hxc$ z01$l=EM%mhl-bahH{S(Pkbg;Wf5xV0VmvMBd9Lv6Lk-?1=iP`Y2NqKIO0NUdVs`WcqYmzd)Z6#x4mb zaf)6IK9V7ClF7&9Yd?ct9A*|rVO_c|jQ;`oXV8!Z*{1m)=!nyI4D>^dS7qZ%EBNL7@)hhwe&q`G3VUS*zY9v7J%U*voW6d!B!x+EzY&e~;(eN}po5E%yo~?PK=mmNz0|u|(%_ z;#6j1Clz9plj@^1X2|L<5rfvAi}Qx&f#+&c4bMeJPWUl9ePcF^sI6WlU8|F$YL%kr z%&=y7_F><%X}owg=z-BwRWP0%e$05Zl^G{hujGai;#BEccCvbvVWfA=&7da?F~OlC zpf+FQ5IhRQ*8Pt}4v?0ThL90OX{4oCI^}D~nv-J9W~CoHnIle>U#qPiPG>Lz3VjVX zf#WQcF9NpEWIByd;|y^&GDeuxAT!BmG+K-no7GO4x7rFiG@nCfjYDFqPuJ|^>`|Q} zwE|C)57rcdmi6BUM;zoaT%6wb!GDhT33wl*ilza0!4UNKC0VshU#77mvtwxuc$E^1 zlLK#KC-F2atNOV9Havms7G*y$2U3K;xqH%%im_lBu%+OTnPY|z7={e=03#GE^ufZ1 zB*Qz1E?Cexa1%Oy5h>}9E!L3jHItIzWJ%U`1a*Z0d4Ja@$_De-=^rM%o(-aZ%27HD z_^J~}Pw*D(b9^vj4vedI2ga3zS0ML+1~VIe4kpaOyDuGzexmT~>&-Lt4m&&wA5H< zoRcd-6cxQ_;LNEA9N~Xi+83_e3O^61jrG$d0_`rmn@qw(zdyb0 zLsybAoe@1w+|Lu)j71D%#>ynM=$1w7+;edi#AyvY* zz*%%REslc4K{=5{Ulhiaaay%07Da37fexabIy!Rt^zJyU)3DYpbIKj%`A%DYZelE8 zn%D)(rJA}7G0hrTN#$AXCr1R5U3f8+iIHos&UT6Lc>fR@xgr61V}XUzQs^xVaQDUT zfr)QKJ&^%15raGdx;nX9A!an47uqGsn%1o->fWimRqxil9ob1OlUv%YacKD{SP-tl zIaS20IHKuLmYE|*u%Z__Yby1P3)D&C3d9&zfBysTr%q%e*j4NbzKpNo&#~+LvyDKJ z3lrQVoZ;nK=&!>$xTEf8H?YZU_L+Z0c&PuyjU;mr-|JF+IJ#i@q!3JMWK9Q2A!v?X zDMaBst!a>=Np=C?@&Vb710qIK(BdH9A%XmVn@VIQLybmx7*uMpOB$L%&?1voKN^?Y+7z7uurZ$yV%?CR3Uq4*Qe_H$kYySFK)<)q5Deo|^ ztl~fOD}Fl37BKUF*meKQ^*k1xWl(0k@hM7S&!YlxEpwi){gHD%dtWW8VgH7D*n|94 z{?YsAqzHS(Ka4*rEBd?p_+v_rH+GCFp-8XmfzqqF-YpoV}-#>VZ|04-5B4lARd`)~f*aUU`LY81|OY*zF_*AQ0n^Wx*Eg*X`$&6~x!;$4DX zULFIW6gr6f<>gJ4k#d82bkuK0#SK!wu05(JQ-GEVdR>(YsHldmYd}jc@S|eF-j%%k z2>;a8lnV%)n(Qp}d=y$1WtC=^+O396Vmv+g```gUKuTlRav5} z0R`xkfIOWZLN9lZSQFx5@Vxo-Air-wXE_bO0K_UPv?i986!s|Uk(r)YU@frQt#-{j z0N(((4Ls*$_>16U6L+ul1e-3-YbMTP)A=F0`qF9jB|ASPCq#{g?;AP-<7anuC*}R> z(x`G>us*5l=6&3~%%CQULTLw!!G@5wcP9=Zs(PI*A&y(Sm3A?{aG!aGZAgP1$ z(7==j+JknaTmbE%sYH$G-bh4!Ezg@NJ=$YBkBQU5H zpfTl0_CSE~D)*Rz6y(t%DU2zI5lS*rb(BhYwcsW}oe8XbgEB_=IZY87U zn&>TEVBHddjaQ5~E)a{r_;tO(T8dl`G5Seyz!u%@=@djxC7a-EJpzE9+nv`;!ezj-U$$rtgfEf(4>-+Zw9TkMv5qO%$% z;U)g{)_wfbzMgj^qo+!?Zd2a*lXlv(2fe9^^xxWBR5i?ZFg4xe(l-{!o?%|UDf}puASTVZP!<5BeZFqYipZJ z{xi53wIstVmd&yxt>8RIkQ`d#4OODIW%=lRX47zan(nl?Eb)gmTfdsVa`^k#>|1l= zI=<>xzLcX&Vp%*htbGr>wfd&t&hc-yu>pHOVm&s>YHoB^Sov=L^DF*&&CI^Z9hzM< zd*{Da@>9>UUPuUf;f*K9+%_n)S8pbN{u(nq_Gqo}(%C1Yx8DNmZq)Pv=%}O(kZtKV zhoDSBk@Ck|pr1N&kas8O9{psyDchWlTPO?9t5A_T)nr>-o@Xdl@8KUl6CIKo&N3+U zRTjh9Gjsk5dg)&HN%}s?^%2dlLW!CUNe+3hMV{`y&_x>mr;@im*jMjNa``NGLRZKI zt^tUfM0lK~O+D1j&#|)I{L!;#_@lc4-=FZ~Pb{9BqC;-AHWX>j2QC_dU&-$>3>}(-~-9kH?&-$vn_y>GMA|JsvM2)8V4DGA-8cJYQ z@oF);MrlBP#vwnu8~K%DO;mS`-x3d{~`W^Kh2X8c@lqGd{CV(T^22(dcKw%lv*jJ zcz&X6(CC_}u!4McsvbM)?(Skn8o1^=@%oxf2pnm?Cf#=eRUIc2g^hxPMmBq*nT&j$ zN&m>Hv1(e>4k!5Mu9RM^ae0nNuBywTCn6ir4e?7Yn@&$N_uBdXlO2Xq zAYLquh3k$sedy4e&1%yeo~7h}O2(I_%1rq~xwK}7vax25++EFy4j=9_6f(=cs)JGX z+y&c@gaX=D=qKExpY(-3X9+@a$oKIE-RX?KX$TqUsLsL34^Yf>cjGRdDnRRX7PlkZ zPbPHh(ym`|KRugH$Nw~Hy^mrMr&nBp)h0N!2t8|0@p@;jC?(pO8uUP}-Shlb25EaW z{?6yO@#puS_}{+$C)kOPqO&ZyOl*Pdy3=Bj`UQ{WX7zKF#y-g^fhX`ZzYZ7!m3(r| zV^L)LAinqO1_p(WIEdj$@uV=yfiIrqWLDNx@|vdd-LX7{)kZDp=e%Ip&9|{aHZ-bI zHXviki$6rT#61!rW6E|%nh%nFD#<4$B3kd)4j?alJ`qZZ^@i%p%d%0u-n(bnkn$nA zgCAjm%tEKFP*W00S$9Bj5r{>q!)ur!^9tS)B&9Z0hM*K^{Y{Vd|Kzz>-edgB6|B|U z+gZUYmwdd1Pm-QdFOLrQ9u@c9_S&M$|GjS4%bQuVXaCKsUY>f0pLvU4ngA5yC&GdhQ$2_!Wa(iFKe{$uW#EnOt@%7-z)oFsk;hufWgo##fDNx-~`A*Yt}k}WC}uo1$rnDYt)jJ zA|^=PaR6lN=+4NB*1F^*r`U`sNWp8OB4T`TKYwepk##oz@XGohfBtOc%H;~9nb5J&tJ9p!6lvtpHZJ$ zajGaWn^*5x&d>bY^K>-DPmsUS0u7lwj+ePFiG^lDDcbNhdUJ?k6p>i~i-Q%t4$Ftm z4V2BO*(!d0J0_;C#vPLo6OTN63(RXK>BYO1H7Uqi@@<{7W%W zy-zHT&bDC>S1Y$yKgp~46!tJf36UoyvFf`hQaP|!^gPkY?us-N*(KR20|r9NyHSuC zBU5G!GG#D|NrB85056P%r%F$b8;8Egv{5K&6a@f5NuyaA%~AoHlAvu97jwne*(N&P zVFC6>o*jyiR6-~W@*mqBk8RVHeW%Ni*Z7{DyZHO-{<1xht!YqW z1*}*S1C%%l9)Wf)Lb_qGAjL?kLEaf4)y#2v8k5&?a?_AlhGbvgY7OXL+|H51gi9{! zKX$-aU0EtB$Z6|no0bYpkvNNq3R^Y7mJft()E~f4BB!J}$ZIG{B|2WWCdz&V6(Wp}c4b}c)@OpmB9RKCFP;4l8;v6cLzci-hZHZjAk@6MRM zed_dgrcHZydd=g}+k}BVsOwqCdN;sjfEC3GLLIJ?0pPVQycqytR4s>=0l>6cl_sx( zvQ4_X=BzT%GkbA#NNe~ZftmTqDqED#`9PjKA?j1ywL+Na9-oSQ0B>jmY!9!xj-!Nq zfSy0zOm}Z27L93eyrUk7q}GVxojSH|MLXfjF~ciHROl(zCB?1ETbJut{8q$wy*mLe z!}5I8c%weQJYU-ZDXCz3WWcAQMFmy!Yd-Z4rdnaS>JhyNMV>_diftB$DlU}*! z@Z(9buG*KMKl{)_XP`oLo zVD$^2cH0RBH%lxbv4=28lQp@=??)e!d$#hIPj2AHUrS}hS(_d^=3$A$_}Ld<<6phX z)-dn2`3?VahjleC+9%Z}-1u8roJ2KH?X^6n`^ageZiL%PjM$_9FZP)Ddn+xh8(*2FdX z`X;&%;6o_wkeNX~gq}S*3@RO@=SZd|$J*oU5qt;*NdJVo4?jYaqh%re2&2^(u4Dh2 z_%OT)qu7M#2xUh&cS672H9fCWKDbu%DQtPadTunAKFBuHeY+GJBTyNr^iVf(-ibKx z5FJJNELV`|e_SQ&kSrV|=$Y?f9xGuin~2 zj*`+&WnG4`t*__9Z<&xBRqsNuZJQ%>6ov~|xJTPz+a|@q{s93IP=-IZ5-vzM8~lOg zKsgb47<6^rTUd0nGPHB2Tu0x(bsFAzc$-#^j=3FMSab7yb&CJt<=0MJ^U1Oe?1bkz`Mi4ME%*br@C^svIs zXhxGV7S4tWC0tYp2C4%VD#{69p^|sU_qVBsjeNb7%i4H;73bqQR{je={AhGYt(&N? zB#M(&ubo)2p1xhhd*H`zV*9A=W=Y9HYf6c9Sv)F*aU3vM=B&rK# zWwvrqL$@}yv^ZoB^=X=(&RtJ_Y zIdEXf(gWi0A;|JSxeki38hXrj zR=Q3UXFbQ;@l9fU-q6C${P0p%cwhCbj)ExI2o!myQjg%+ zbBtL|P!e!1O9(%s;6rjq!Hv+GDrQXmoAaNWzvS&JHX=F{MjFHrW!<^|`5KUU9{P)T zJ4s{?@xy%baXM7(TT3<$t@p zV_C~yt$O9VvRmY|5VC|!r>zBbf@P)9RLJR7$0tk`)MP?RL7m*1N`9lj`pv#_^vu^Q zr)@LykBm!M^3sjW{>-y;X5A#+=UtUP(ODI7i2cnY_jT&HXv*y8A6@u*RptF>UR?I` zh+%K8w5yB$ilqwhJ|W4Hy}x468fuo=34n!mgVjDM9>7AeFzKQFQ~-x3nI+m1CjdCW zFmXtNMWljKGjiIMNkgD7X(A~drBU8{={Ix{I`Ul?;W@rLN4wil^0puZS7M`&V+IVG z^yX>g?hJwuNsY;F3HSlYX-5Mn?OzGt2=LG7(EcH-)zMVd)p+7^o^$!B%lQ`ttY|3z zvVaxff9xoIDJtM!3}poc{L9PL*VxgaJm-TbExJoRBDzkogQrC8JN3vZUU*79A|Kw(ccgn5=^IoUB%rY^WiVF=KxaV} zHC0#`WZ5tp9|jqIRitu^AYOwaSSPbpHp+l_3n(41Y8d@%A}JN8!m4W(rdCFWkU$MK z@+$E<`g(3p6sZ_&x9fFQz#OIcC216}4k67H2s8nG))f;V^pOc}FqlT;1C4J%Op{^6 z1 zs`7pOcNTZSAL=71(fNSxSl%8xO--FN-^+7& z_EvV3=WGS`*wL*#haFYNM~Txe)}MQn9esHg~R?yG74 zO4z@GgfJ2UKj;>#f71vjpSx86G9qT6c@>kq&9wwg<# zxeP(RW8g8u`-v(`FFzZh55oD0(@K;lA%PVtqQoX(DadV{>j1#4p!+sclFT`l9Qfr- zCa^S49Pl(+PBvL$x&kdK?~M)->vp<~YNRwE3mVW3UgFSnLo3bjbc5zY?J!uA%TY}E zfmWRVhz{NAcl7+r{OvmJ;C%#y0d_g|It&jcX{nHK5KMxj3z23O*_CD9>da0kkoo1(dK>+>IINFBF0=Md$tL zq;R@umR)0LXwzs|K$+9j`mKqoqlsE>`&)U}R7Z)1ffkhK|Eiou9UlT%raCJA1uQ*+ zhzJIV&@4TWE49gKA+qRe#`kS>h$S8M_4KQ`IGXGhbWTJ|_X)tCjFL(~*IR%%nPaoLY}EpfdUKILBj0`|&ul5IJ`_G77Q^l5Mf+ zam^9SYKX_lA^^IE#NMpV34tmaK+-(hAuQgj;-8O>i`hYNH^9a8cU|)pNfG z$Hgo!&Ce5rZkJzaR!?&7zRB2oGqvD;#koKW!+_rb~Ei zN|^XMHs!TgY9A_E0GqNsQVBbjw(HWqOHn>j2-`FPn=%3^8jVAFc2@|3To6=pR%ogO z_@D40ouEqqAF{$%?=wC$K4iX&@F80jx!UHp&1|0JOwMftK4b(67|X+BLDqa9V$xxN z9ruNvDrBY)ms#KG0gOg?CF)`{(jNEih#-tc+Uq$6=i{ru2%3+#_#&e@;Uwl}H_yw+vnN{8Vw#1)NsKg)Zaz%Jnt>5! z0r0m*%pq53FLV!uRfwKUNR$b}DjW|r)VC|4TY0exOG^l=uuI31?xo#Z6*=3xiiIX) z6-JnAqj3q(z8ZoQjRG0$*GRq%;00DdnXH2sI9@Lw_u&ObiC^U@0c=1|YKZBfEHMD| z*NC;2AZxONF#9OXp#?X_!>aXT_N63IAU7i|DLW-Q&LZ0t8#p0=*%#?lC49c>bsEsT-fCPX4XtV8{u1+DM;xCe}P7J;r}h!%B^S+*5#qiGRQa?Mf*M=mQp#nH@>5Nk*_Qm3xEIC2qYpj<_map&$2Icg06E(}Kn z#!9$ZEro_yZXmr8!!|18b<+E=+;VfQiG_JNEpuC@r6gusv%+J!MHqKz+%{eidLDH_ z*uu<+{QPy#(LVL@)B@a87f&sWI0}KM7GX+tvD3~chMYcTiM7BXxbsDO>dG>b5?#^RTqTN7f+jRPaw6i!57|M=FFu*IljTmV~) z4&XZ2V&f_6sSdVSVLoAtwP~4OTv(i)k&>6{Y819u1Zv2&p>V`%=7yMi2vD#O!c8oKtBz>cMD zySMB1C$O#}5Y*gMT&r`PLr}SCAXSILv0l)*V+6K>lg?6#(gSm<&VhqtG#GQLc{9SC z%E@fzZ0@win^MgwxTQYkRHWVK!Yv4Lra%x`ka2`zdhL=*oMLMj60< z3U`r?{X~{{upJ%|`zg|ThroF{AKDic613^3a3lz|b`)MfWTw96W<=;g*WG}16K3)1 zVlYLRP(8dQbw!9JM0CXmo)k_~2-bOYJ+2`Z5>5!C){_7>m^K3+77{6Pf)K3AMZ`ji zFq??DM>YFH&#f+qQMeffcr*?m3`L>vi0W)IS|TqVQBD@&5#>9xigJollj1WIGQ#2! zMVMQ7>>+hZ$cZ%o5(+o9Al}pcBn>fvV1MdWI9?qbIv+5B$VC$DAZdUJ6j5r_#s3K@ zJnEqNJlBZ5qmMrnHcw!2aM*;v<_YcYX^71eVTFQmc+PbUu_i&FI$_r$pnnlQP66uT z>fz&1dX<(`r8ylV;^RcVRL93@QK;kNv};{h(xSwX)vO>bzp?l@k(M`P4V#XIQ~g5- z+B6E;=8MoQ5ZIgkbFUsoO=$l~7~vU&QDcrYML!-*_4E)E(h9(Q=rUoKJ817^04>;6=cb03cVv*4u!IP# zVQZkh4Y(j)ix#5^#%sAYG+v9lJK?qT?cIHVdq8=IHr?8GjRsyzB(^XvZXz~IRVd$Z zV~|#kgx(H8wklz!kXI!XW=cgU%oML*NW)B_bD1zxf=}iPVx~mkC3#F;d=yV+2=)mD zc2eCU^h5(Z;}R&27ZJCl0uHb`xFy6ezCuW-R6ZmO3hDtaEGVe=hjB|Ha!I2xN~*?$ zAkzgAM=s183BtUmYBcf&G0r8pDV5zi8k^J-c`8*Qsw* zaWo<_#~;Vc2pJV=Is(S|{Lowy(C_N(zYtg!B(UmWSzH^~b#<^T8o&RVh-DFx!cE7m zs0!tmH8mB5Vrgi)2KZOUlOQaZaCj0I)QjukNkqQB4xU8hOG)Jho)*Fev6z4iRl@)g_ z>Dr>e(Jr@bQ*ad`tg^i40vHOOQ0X8S07SU#!fUH*ll8F;Dp0*rAKSpINQ$Bl+aQdW zCoHx>qy=w?V^B3G#Hu$$C8%i^ZjJ%DOc(>S`t>mee7OXEi~+x{teImdt**b=O~4q4 zwAPKl6Q~KL*IWo3;DIL5qE4N^FXhBv=dVS)DE8;8^KVFtM%VdAphcr<`fDB!!3!5E z&<{t90AEnu2kAzETKgbjAG+3_c%D);>R3>bYwaVkecihHs!(Ze4XWfn4YSCBc~QN4 zXX;H7w%#30P<2F2;&`9$A*kNHTNl0Fy|=qd-)?<76u0c$s?(pScaKDZM%A`geHwxR z8ds?vj$A=DHPxL{EWBRbc}1vt@$rqTJFmD<-MRNUL3QU8U>AhoEXzMtcOH@EO{y`k zJ{F4jkt)UygkjMj-%3j@w@k9bw#o+UIkZg&cX^LgvY#Ngj}2RFh26&Ol8v^vuvT$)mKIWHb~Q(e!7iK~MJ^KIFzNN}WPpH_XUQ~O&Q<#x9q<#1r z*-(3c2p^p7T(3qp0o^n$5c7N6lvV z@%w8wBaJ_}KC}9n5DU?u+A`b>>eD}L-6XIhg6k$nX?2rhwYte+G@IbHs9QG~(J5-I zTO(QZPKe3Wsd!u)nvp`xBXsQ|nTSEPi;?G1uXZtltf*V3c-9tb8bL*e;g%yvN73_w zL)8z4l5&(99Gn>JuOEy^{Xnf?wHji?(pUb|J!ri8x%lU0b5IRm#Ic8}*Q;(8 zViqCG@8*P=k0=}y1j7nsTg5?RDR#Gk`lrZ%`lwS(BZ2X?_?sZ4CMU+@+E|CxY6N6h ziGGfwPYpG!LkC(NKFo1URy`frZgvTqzLak}&db>DdpC?du#=aW1k}lV#NXuq+rWQV zz~qW0V;|TFx-L-{m~Jy=z>+CM4*x;7O+q`$OeQKxs3S=RZh;I4-YlSMwF8Pg5amr% z1)#OeLHq(pFNz4)v_bRw7YxD9&7~ZFEq||tJQS|e9Nbszy-!B}*GAsx?f~K_20^L3 z@V$Ze!Jpc!sK}91)T*de%NF^roI*!oo--*gF~*qQ(q593+FWb4VY4PXk`o0-dsn#~ z*v&aE(OQ}>Dvna>^&@D&GfU3C&$hFxfBA)7wz0;6B7>CcnRH99%lM~@`TzLT&ptUJ z{vxeBbMQZ06<_8DmCsK1fINNZ(2smD|M>MQfTp>pv@KUV_Wp?7%VKZLVUEk%u^3j& zH=>vIaWz9c4q71uiL=FMvkcWLJX#9K4F_zF5C=6+91LsGVl)G5&VW*Ky>ev|<-Fw( zg&h*wLnbHbDCFrV`bf@pTJ274q9}kS#7bAW#vwF8!A>&1Ls?0CV2~LcLEkeAD?a>w zB}<&j+Vg!k)e4XEHGC#xU-iBI`o4oEOc0s(8TSNHJHl7<8Ri69dWA0HtBMa~6JZGiv3521Y6Qj~k1E~H z?sAON2;)Iy6$sHuW5GbE_n@?NRNRx(o#_a5=*MBw@L+^^7H{?rntBG*A2pds^p zti8Ab-C|dN{{!!*PGlq4RqP7B3{3PKYsWv5w(Xa0Im65OcD{{wI0qol``Ha_GMlYF zzaQgd!mlV!`vv2S6Yd_g#-ectvgesF6}hSefrPlTtc|xlHQp%^Nu;#LWjVMRm!V@% zxa+`YzDq>$-UgS+6nrt0QDn6mOyHp)v&fOG+{S-X-{yN{?kSgg$=9eKvw@y}Y6A3O z7w9Xz0`YW<@hHZfB3wFXO-l?Zi?|kuzA!jNFNqD*RfE!NL1JQSZ)u+I!r)(zcE{sS zN_=vz)tZ-M1Qu-(1LkRIqCkuTL<6fM*N}{TZMa31|I6Q6j@A^Hu$7)mpxh)nr&bo*pV73@7W@Du{1XCFs zBO1y7V8lQz76Y2A84TEOz)TqjfIX>D!st6kx=Fj-Vlj;aj0%ncMRDkNgVAvFXgA6o z@cM#0rz6J+g4><8Tx*;$o2X8`LtN!ZEz&^rB}|q6dIfc=l8?6Df@B<`7G|;$C%3W| zZCLx3C;5vXpXaxoPT`;MJ=jF;nxA2FPw!_}{@Rl7Z^J)(>nFZ8SDwdJzLjs1WYO@J~t+}l^Zc4OTlN=^vbDYY6@wBDWwnp0;lmt8Tl0{=d zch-&d?K<;;UNKH3f$vxQ!XB0L}Z3KG&9wvFa|m&RcUZkC3+ za_z00HmAcBlhu+XJ9D%XqO8>6Yj)d#T2kPI(0WlNJ2V_pa9bB9PyK;U{&vO3``EC( zeARCJ`(WepCsuA|efRHYJ>KAJ-(CCmj#YbpkO#lT*S!ABhS~3N&#XHcdw)w+mmWXA zu#V5Yzh-&mg5~pPy-oXOq57b4j;3q06+U>QZLVknPLB@QUy6B3K%lDBK&ZsK3He<# zOaQ^vx2U{u2)N8h-MCsBNgiZ?rOQIzELUFWZg)wz8%=`IWSkJ(A7pTGK-Q*06XK(Gn_^W5-m;-&v&KYwMo?jFV_+;pMB^{Oo+<8HZS z?vf>4d-pE4fN$1APW3k)MIGWL?gTg*sV^2aRRC9j>?F2QKvr-137u>Kag~JIaiv%h z{NvGXlyp;7Bf_!@0oPg`rDhEW9;c(BfAx*xJ?bpwD$f|PmHHX8@sl4*=~B)Ceo}3f zrWWJY|Bue5yVMs9=O7bug-Pz1WMJy2BxsoWgVwYG-CXsd^`VB=jrd+qx{Ra#Wpt_m z3EOuyNk=mR!THWa13=x&9n?Lf%vRb#bP>Wk9lmuC_QktcXVd)u-naP2Squ4(Z>$#Q z)V!x0PWTRL0Vl64ANAl)uZL9}!NO`WW?5u_Av z-nSP`+!j^w#TTZk*MFwuV(u~PzWqGEpN$0<$| zm@3bExRwG{z1+`JUV#Nm0bPrXCRB<(`rq?>?kC&WrC+N(lmj*IiE~!3=RYi*_0yuc z-^@4N@CkqGjj!2#H7D3jhhme&503KR64l#(f0Ccrx`dy3cFOzz#EbR)t$c-fE5Jx- z=WZ>)p#~8Gy$HXA-YklmYT;l*4X80Tm<<3S#I>j{HX56G^)T>#vhtOuBj3aKNl%g9 zRfVQwHQV;@2VHm9erxEYoX~5Uau{?jC|)A9x-dwhp;9v%WCILoZ?X9#P1PNT*CRUz zU!8)}Uf{45xQI5)8qYM%qZ=d4`5YnDAv(~-CzGLF8FpfMsiV};so+I^e)~cG#+#d1 zpO0~t_etfSn z$K5ToTMXl1Z(~YGrWgknLAwqdfwWcI(p{hgI0`4IEM4$6ERF9DBcRKSCavwIc4w&9 zKwpk^ISX7?5D)F1>C|gLDMwvuwogedbF@!Q);eD$8@llQd3@9DM6_8%v?s)W<-f?! z`3d{f_alch)1ZM#ZR}UwD4nQ7zq-`z)$LgF7m{+ttt7{w-jr%yZ&$SbGk(GDodl?q}geW?hvpi6a}Lfd=a|1C^*pPq*U+b zSuUpVJqA1c1AGPlZYBTULbPdrb?##e86_DZ{(dq4l5e>C*OjxL+VmQzEo{Xw7bqVA z2xXwIxHQ9(2@1SC1VWAg57Tx(>S@y5Dfl6N3oVo)KmK39QUDXj2r-)NLY4xvI<4dm zD#53;;FC|(>^*ywk2v>~62P>yhl#jBLR4QczG8U>nG0l@-?$!09B7)M6D*r-SW1D$ zUX}BR&vbfeS{HQPbhoqK6_TWFV}bUS?PWTj#n2THDS^~Q({?t%e{2gl>Agw8wB^Y31($d+0nk~wUAuN2+m+kwE{CgI zmmG@`#)umuP1SsOg74ooVBDpBuWdDT(uOHa`p@5%zxCyZyLP|7di5rL3%llrpHA~TU*j`zj{J1? zm9y?nXE$%)Rr5yl?0IFkq&?2ct+O_7+_+%La%NmN=Ya*%fTKqf_`U3|1YNeoOw`V@ z6ycFUYm%{C8D3vPCCL+V0by%iBcl+Ls^Gk`xS;Me;6&dgLm<7yasJDpM(Pd>@6+hY ztF=~+bfIR)-K-fLc0ou&Tj5v>fH}0olE4miA25u~x{@G|ZN>KC{ z%LJIIwLg*{tf^vutF2W&s=*nkod~&mS)^nc zjRNT;-Zwhx{~mIX&IY5=ci4`0CkX;tJSW={fo?@o*zW5&#++pIvl45AQ^I&DyuQEK z;j74!ZFWOq$dw63;b?Gfe1KVh2-3$0F1G_yf}3@)hJ7+=h&Dt`vC&84HOu^?Q9k6y z)JtsqOTce$vKxeIK|0|{G6)_?Qb9~n_@wr@*#Lbi9d^L``f-p48XJrXAYx2tk|E;g z6vatH;$keZG6*)#`%16v-L2lm*zc2uXg6wZu)uwqC(QR0O+MeuAl^y@2^l+zBv0}^ zNHCklaoS^I87&Yo1nv_p7T;Y^Mlv!ofOpg~qh*Ui2hyD|{B)d%xdpk7_?S#$4QO%H zHb__O+C#d-X-O+&Rjefa13I4B;CW!$iYG>X$#*ST!pgt6_^FjsrQ1BG#mCee`R3vC zM&AA6uwgIVGwgxd+W7cgY}DsDU$9Ge#mCq3MQ|}aVv@d5VjuYLy*c-NGdBkGT&Qj{ zeFHh4j3_heHAy#%C-p8X69Ap)H!coN*M1-celS|U45CGWe+ir^!h0jj`fSPTq!|G@ z6bsUn8I3v(FB$Dd?R0KVdK%uHT#%>5i5MnqwEDsvx+r%q{`m$gdxd|udj7Wq zhyC*CYW~afVjpF5b)V(0E`OCiUtO+$?&iPFoiK6Ygt?5YBdk%a&G@$I7tpCdKqf#= zW)gYF^HXEZbbP_b&v53#_W}1OR41fp>7Efu7?uKGW=cUy0V*s(2wGQA zW3sa}U(gyNmzUa;%LLT27vN_G`X;qhUj9A1cK2@n-1n!y=g;oh!zO%x`crDX_UY+Q z`Tv;xQ`0YpGWi+qhFkL!-jkor{PN381TnU;)^M?JV={F}w&x;nZBzb4+oOYGpzB{bP$Ste?J>tn{UsJ26wki3k@3qDQ{M7pGYQ~O@ z%U`9A2(K>Rv|T*DeLYKjgdh7{&G>>JeduQVd5GnFAs+vnWk14ZZ@g$^uYqaB&py(= zXM1(!;7-?;+&i!A#?cz@(VpmN{sMcVg>ch)8xC4Jp6n14F2Mq{4a-!)1Q2qI$ylN2 zdxdliX&m|CG*^6{ewlzqw-K&`FlbR!SO8~x5^XPp2FYX#B5PeT`hH_~lsZx!$xeBi zw!b#9;+OgNtLJ|=Xy`AG(Eif*UK;NvKPhu(wefWI2yK(Ct{y>~Y~q9o6S2v5EBHTc zLc`BXmaHM(ge-vl@gK;7G$F^GnPN8^$ha8{VMpQs$3Hv*$tfv`GL%>vrzjgC0W?(> z|FfmyKk$ngma~#A{Pi#SzQcUYTdeq(4_N!{|3f%0>m+~mog~#GO7

    (o^C$YSvHu zbcTGoCbl_~PlL;=;BCl(&OtGoq3bCe_xKv05`1ZWVtU*l>6 zsVJJ6fFD9%@?S#%KHqOgyHn%inLq(1Yl5ElgfMd)VcvV=OAbaVxPaqAAG}nl&cI;% zvjOZP{`9||iPa!-7v95aglzOIIU zCV#k?wfpTM{-(N_-}4BcqkfD~YYAJ&rtx0ih{o!7)ve!%7d^6&@73wP1-9^5=p4-< zRe;z*^%-&C28sjz6?l8OcB8Tc>kAwNLrhZ*PG^^~GZc`Ry#ugS{+$ zeU{lXj>^`Sherk zszqMA{C2rH8R>8g#KV1v`NbK*F<2of*(S>HGIulcRKn)63?DkG&?{KxO__gvb?wfI}{H_Uy)ftQ#6c#r4D1{o9?FsD27Z=U2o z@QZ%?1|goTZChFQw|h94WbfRjCb#8Nzs-A+DTd2@*%ZD{tl>%ZxUnVHH%GX5(3)I` z0&*HaQ^6VFbsB&vLCA6BIVeZw4ao}c1qe9Ef=nR?-6!I68srE$xwe*wenZ|`NnjP^ zxvZto5W&NC2OXy(mL16`)+AZ}X-#QQK2*KyBR0K;^<_`w@O7CVZ$87ZgFk1z_^;}5 zW;Wf@qEpHPd&K*9qTTiL-}1-!)-QJPb^QHZe9(Q*Pv(3LVws3LKn}0OIY1cV?cufr zm?>6Nses-KwTqN}=(w7(N#1i)j0t^ea514!nGkX!1(E(T_=xW^6sz~}56MX!z?SkX zgPWM%!>#`JK*1(gOJ?95nqR_!%oA_nzl(_09RCG>$>Zx^CqmvQH5`sHXgC;>cR1q3 z{9s@>Rkd1LiHLTOf^i>aMEPEA5daX#zefj)KVN_Z3@`l_@w|Y4n@f&nWs7X3PCejZb{?{x~A@Lb%$my zJQr>xkF;ssq9~`>QJfFlM+%ZN$P$MyJPP5$5bO#HpW3kf3d&Q;dXP#`f`90NdEgWu z%g-S=I`-5lwisHhnJqrW_bui>u35u>T)dd2u35uU7fYjh-;~~e=8{f%>OSUXvlH2D z)>FN&_o+r9K2~o^jy;px>E9C#Ni~(D%teaWLbT>B}Ad#T9fKzqX z79|4M6D8sVeYD_%-fXN0$5<*GtETY}gMo{PF9XkuBVP;9ngu)+);zjiV5`CzSP}em zi^URWiL>J+kzCi(K48Lg$Q_u8_0ATi=w_xR31XEE zPG)Riy4!*enSLM+_FAE-nxudUM2>T4XOJn{VL>uQ`918-)!9Z!&of z90{7=96mH}Dh_e8cWXfdO|>IuLr8%B29eUdCA`rXB-{hqCNm5y@wvdzRM+mQ1 z%;zBG5->tS2?Z7v(i!8;agtFqpRMqsEDDA5(p$5WLJ|`1WmXgKfQTkVr*<{ zpoA6_JAgc%o|l-G*bHz?39$(_wtqF|j zj4jhd1}ih%2lVGv?1S0J65Zvje`*Efh#t}KOA`5@?|AZk60hZB;@?BZHVIufAf*T{ zQlE|;rz!f-SvB#N?$-X#gxUK4b;lmMFxhP6*fWci@MLA|0rr1*`wqaUs;=96?wz?a zlRlaB5R%EH5K1T^MEcOBlYl@7Eur@g7NiOS(z~DtC`CkRf>Kn(LX#>YqM|5@fPe)B zlDQ{u?Q>_6NeGJX|KI!kAYtZ~Q}@|r?X{m}&#=DGf(~jQL-9D%L)nevOCw#Ul$LD%8F`94U9|NU+;{iT)_AOihR%9c_|I7z z8C8R5^Goo4`TDY6em*z^efc7=?cL0$rHytbyzh^aGGFbOm_LfjGqC+EVkyI`#uz#4o@v09We)D+{7Q zc7!GREB7l5M02;HW4eMzx;{@uc#3=8ZBm5*jI8j zwwX1MkMa$N4(bg$%`5U_h8^rrOk6=5!`t^@4(JV&+iN@MGx;|fll|ICwGU)0@l|O~ z-z3a!{c5rgu5P_O2&ZX7V$U(EhAD{SlVfR$WQGqHNNOCo!q09rnh4P(B56AP5MWL| zK9*egLMvG)u8@GoILm$vw7^Gw4%olTY>C$+e1P<`^n);ZphbeQA_fX}My>R!sSk2z z`9Zyg$WKbvqoYtMfnh=DCb?D;so3@g0)8E43R6v?VZkN{uEbKvC}sYCu-}g!{h_nSW^^{|JL-@%M;%&rh)*vB7-S!?p^Q z926G}!Zw7&_hV35W?v)nJeRM~H2yU|$uDVt^VRY~Z4JDn zuA8jK-?xt+yY*N0;6XVMVRMx6uGE6F%3#-W`eUf3oTkrY^Xs2~{(9>%qtPYk$9kXI zp-FbE31pJGd*#o3gZu&k>h$#~?$dzMeR9MTxpzu(d=*EP7!c@(+e`G#`p~{5KV*LY zy_fS!&*#It`nzZLJU?+}%StyxoZ%8AYLe4I4qG`Jvjhc(4p3-#dE~~GvK;Zj@$mwr zW=j?>R)WW*ha+|ypDJT}fART&7oM0fta<%5rw+_%o!7cUM%~s*H+AEuO`3LX+OMF_cMd14fqSA3NO12 z>p|o&Kz?8mxJG`&D)XPY(yd*mu8eie|F0HMekb9a}J;MC}v9a3XcJ+k&c8R&}b6a8g!tWp9etN&_zW#n&(fcn$ zf77vBu?Tcywt8V!iU3>qK*XL6ML;bbq~!r<;d}JqZ|Dc{IHOIHoj~zS!F(z}i$Fgh z4Gh=l#=H?W6QkXY|4i}xGgm#bl>mXhcb_b6{7)vIu{s6+AfEk7&I;%k7Kz9TaDxjd zEUlmOwm>9DNq$Lo6|F;LoMHtODzKBatovgxm&KC*AR#{b@Cy77oY&f)G9#6q4 zfgGEJE{SNcIIBg7zJj5h4hB|5Hp)MPfgc(C`)^=h8tYnE_=t2yO*I0WT zCvGoAv4;ZB!K|26jI%$<06#O#Q0zd8FiWs|J&} zh+Sv>wQ2G+zLJLo@esaJo~IGs{yW9)l3cJ;T-V`;Dw0dIuW;?J<((1IQCb z4qX+oR}7rLKRO%a9|W>yy@ZPyea51)x_hlyY5yP)S+Y_KE9Y)R$=WPtcWYq#L)Z8e z=VcJQK{~TCR#O{v-(&z#7U!hNh`cDKry3}EWac4y~1&7axe_koDD}rOFC=8`j?M)Bmj@ zJ_%mupj>zodv6=o1Z6;>t=PWg{D7i~PzcOelfg{6iZEM&j_3__(Qa%P0(pl7sB+<< zq;w~eeQou{o=^zKOa?Z*K^}vZ71^paU~t!BZTQ=zO;fi~YW(#SYsUAV;uk$WE#t4+ z`67=N_CdiO^-AFYoGC`B9m(C++kK%eheKG%Y!p_Lj046k%p|!zpmq3$&3xdCQt%QX zrqObTwP263xO^q#+MX67>$QYL0Tw{3On zoSfG5XVH6gYSyd+AR+DlnM$l-wkZtucCu(kIb;rEX=5?V)C^gsV+Uq+h?UH0dNWpw zEj)t`pK^RKGyW77cAY7FxDu<})n4r0T^``F;JI6sRKqq?GHz9OLFU%Leotpsqbb~I z*jAt!6m1DE9j2tRMPIXdyzrOMFi2{Q%irp0e&6S)xKE|~m^Cp2NCXy||F=9UCr4Xg zP}I}Ra5*gW7smLQuS>m4++*|Hv(z1#J1(Y_Sq64@9e0DtFOsjM>b>OX?yu1gA}kG}~!O2@dpyJD7YJ1pSH- z5g^*1fZ_^BsR{uJB0rdC3~@-UsBfxsqA9 z?V~~|+t`xdak0RB;w z?b$eV_8%{={%hXwEqhx@IB_v1<^}FCsY^U~V0fyS66lM}fSSQFlHUQn#(5zdAibd; zC=jR!Izgrl0%;{cL~wNBLu0lWF){|EOp4J|t6H+778S>e^wT5$SSG;9=hE>+ zME=?pXB6(B%3XPgpz$C^d+8+EGq*i3PiI#(OCB%#@vMei_q zHs9ZP#gx(WD_34LY|@KOmD=^M@>QP=@4MUJ9Mi^dE6O@#%;2&65fcBk{?sq04)10# zYQwvYSo|lgw`0=Vrp~4aijqhzqz{}m5QoJ~V-?M)_e`V!I|{J649afrKYEz#8bQSYS*js>z}b2!eGAc!jACDWnP% z4FzTgwr3hysSqFv!s+fCajC`R)8&EteM}%l!6VvdJIl9d-ndcydUfm6O0Qn6YHG!l z#DtI#tN4FVFrnlf;U>ZWnUXUc>1oLs;pu6i8R4e1hkb5*$o0lmU#=LoWaW}U%f2|h za1eeOw&)ByfX^1cxOgyqHW%WpFFi>=%N+$iGI=0C8y{UtS7ATY0!l`V2xJ;&l$8~e=JQY zG{A(^e*#M3A7XI|iKyg4rXk%h85M979E6M@7>;Kdg#p%!{fcl-Sa^9P%h8#aUK80T z_hm)6nJ#y4{OD7*}{;ngd(qlQJyP4F=y&8S~?w&82 z^sb_q41NKv`sdV3Z7RlCzb0p-LlHpx842^vsY}{ zx-9#{1MO9RH8kSc8Cqe$OFH>_js4sELf7|eG$eImg{6OGs{IT7jjNr5~;K~h902SMkOb8(yqD1t}{ zsb3TaPXnf+_4aVwkc`=bd^VB0!gHgdlySK;(Q_q4wgRE$eTO@|j>ke~M8}qAQd;#Y zl@cB0QRa|HDPW_=uoxe+2(1fr7Q!fqBLga6AovwY6*aU0v*`)Uu*HIaH6iDL5H!kp zW@R1M^E2o9Tl|$bS>~M+%+Tlj)~;tKSLk}b%i=FL>_4zg8`b@#`h#;j4;-@AXyCi~ z*`xgAR(6eD?g?e*Hx_baSNH9sVp3l}pEsIo@9jNs?b-o0US8xV-?8#zIU_Od=-+X5 zqoKL^PZ%*)Da~08TiuBBieMd3(O?JwQvhQ%TSULe;>$z~)*4{73B64Pz@vooDG}$e z^2G1IojiH_w;m&h_u^l!x%-Rh+>F;>pHaV8uPg?{vv}v`|IK$QX*Rs`_9@Rh|N7C^cb-!>8|qoMpr0`m7mLA)5pf1Lh{XiQCIKnXWdNHV=rj^Lg{Ep& z0mMrao|g19F&K7WxWT#)8P>haIp4nB7@Lso_vKfU+F~F! zZJN;_r+cs0l-PvIv6F}8v}q0v3wy!kuQpZ=n_QTM3SytckkADpywvCe5E^7aa8799 zj#Ne=V%6lRRhil2{Frk1uNtTM7yI|~^QUV5)dJ}NvuT^MSu8+pTzyQZPGhQ%IyQOI zG3-kXF2Rw7<1ilo_&%EE;`iyHN{8?_@ezM@8#pwZ{dN1Z&wkbZZaZdN>&;(&Db~~( zb)CU!8Hr~*s1|e}@*;pXGe$xIQrZMCwH5m29(UmOk(Gv>i*Yw$2bbz@d?Nls6ZCQS zc5~X`V{^kBLyJ9cO4hAkKRbHsX7}O4x;J`ZdcRh^o~T!;{@iK(>*eLvt=Q0X?)j}- zpReDu2fJ9Y@6zhknx@6~d8u0UX0Vm4+9-auurC^tN#oyoyB|bF1P)e`cGxQ(YpFNZ zuiOC$IPn#LWu?B+bAjEy8~`(fT|*`^{uKPp?*8Oz_!1FBaDRq#o#mg_Ch~=aBg=Si zDJzZM3nNXux1^aSzA5XyFeYK9*{%7YoVdcxU=6&({a8)@nIz|Pbv_?ysg5~{l$wbZ zR|jK{a11j*K|;V7KvlvO!S6{ugu#Y?WCkLh9BK=Q42<;kp>UE1E+>{_kqeqEH^t+) zg*NKibNr_8Hp9&B)u~UXuDuxRsm}lE^|oWjwtf98mYwTtmy_GPSx>C^d78iBNa0hk zl_H&CkkB6ch2~*A3}=P%s}&tokSs60$NywIz6OZwFJ2hR7x9VmP;DuDE{*n-G*-(r ztW+{Ew(2hU*kP~9CJTUPhLzmOazEkU^PfLey2~GFm9>*Y*%AKOSR=gMEdM9%1fN}) zgEsuL{TOWb*in@29iD*%TGtbNA(M}vWkxxj}0Sfa=%`u;)xZ1$dnM1+S`07ygwkinZM_NT5%6#Vhg zM=X@jJHZ#TD%tHjcI}qgKHGGD)20Y4!fH|Hnq=oRYSe{}W3=(S`YvB=xh~pR%b6xb zM;j5n z;9%MN9R+vLxez}81YcsV{K+Q~d^77Dg}v+pVBn#`S$GEgwR$he{B=JBT@}qW;88|T z*0U?3D5d$y68XwK)o$kf=FZqBHFx_XRY7XmdV4e`jZ$t{E*dqGwB#T>Tw>YyC!v9u zl$rf@qFsiO%@XW!#$<@+v>JXa++2%y{!r|hf1qSN?W1t8U#6n3uw6bt3q*|(nBJ%}ZjjD{Mv%zV-K2}^o) zHSovR2VeSa_^`D7SJ+h6)Wm`^wqG5!*A-eK8cGW4QAskcGJ80wo^kpBq7J7!{=$q9 z4B3H;;16emd`&(`8rAt?lG~z$pDf3d_ZD7$fWrU@5EsaF>bdVIaZR=}hDfY{;Y8(T zdnKL5iX4ZO3*txc7D#mHk8u)Q_&SCt!*s$5!x*m%f*uIs+oJuhb7%4&U(Url_oFov>$0u`-r zK48{>_tjeng+zrz4P7JM1t>Qd-YSJK5ln|k=0`J*hMF;l`nXzH*;?cn_6onJx%gc` zwL49Gx<})#ZPSw*J~^RBMz`!5NewaXdok`USoh@2=c`t2kQmotPL(PR=|qs;Hq4g0 z0qdnmaYCyK#fKhy7hkjX;5u_VU&&$lHrx1dc_cmTrM2=l{?hL?Nj1dXWUB&fuMF^d ziYE?C2@AA%KsloaPvHc$F?#05Z*NmOC8UMG>cJ275XG`1B z?wr!&U!w2sxwIkQo%(OGZl62)&3a9lu@k(IEVa+zo~I}Ad3iB&Uf;jhXuZ9 z;HOP%=Pg)O=do3M{h|?bug^WsI(;UuInFwsSXi*`sU1r%kJa+VU550>_(~15&GJ4L z08~d)rw=4DR3~@`A&}|B45D2>1HEB+SoxJG?FaHge1xmW@S-ndmX*Cb(IMc}bmRk9 zg5vPHvxqHb75=!K+P-o~SeMAmK`ekT`I+BDEeeChXYx3_KTG>q-YfOMY3gvs0TT#& ziukQt;j0n~Yhn_q*7gj_cOn;LGJey&QOtkE;_08x>-&64weZZEUF!7f(fUZpj%r#Ig)IKnOgcBiB$h-h7 zDJbx`X?!rlEkfKO3Tq12+WpK-hE8fBv+g?n5>3HzV+%t|>|YB;&g~%y#Qg@TB_VR~ z1RuQI$L3=r*=g{|PAKXmJ3%rU>XD-InAxM>Y|xMlyg6guY|zBqyKoji(`RU}larWS zIGdmCGqlI4$ygZhDQhm{B@s8NE_L1wmm(%h_gTi^Oe6Ik2e%^A@`qy?mPNqvhhqqG*Es#OvlmB3O?rn5cWo+>9NN$v!2LohgfkW>hTGRzRJ=m8Cp1P%%i zB0)07yCnGuYxdA;7x|5Ktj1RtS=y^h`Je9skv5VUvu8d2%8aRNTP^O9(rD)E?KPJx zv_p@iciH%#%l*W1KK_8e@qM|i{L0=)nf3j(pP_NNMb~CM|I3ncYp>M}n0ii9@?TWn zDXe563`?LCE!7tDlnMtr6l)}gBF7Ws2C_8@{7@uOq@R?d8OS?iMv7$9=D`mp<$*bY)cCB32!9|QBcdasqawY_ z0~2Ftku#)}dXD3o-D*B)Q1e!O`e>t?4I0#}Ro}inSS-untIaF>H+#HKpU0c^_xxnv zGbcZYF}trG+MmHB~chFe8Q9|7$-Aw)S&qF!}?fDW=9;Y3MjU4FhK2u zMyO=t2bPli!pv-Elulz0MJd`E?OR-e$KpYmJ;cOtJ)y&;`j@sV%o_+m#S=WBI?qqE z34J)LuH$Pg5s(*DO{zC}L|~!26;(TSpe&6fSQ8ckfN5emh*gj$Rza5N5(&zOUqZR4 z3FI76RYY9M@=JKH5x1jc(HXv29?~tVUAL|svbt%ry0*{C?$#l*izQ;?#&B|(MDm12 zIo%pH?2*&BaZXN?MqRO5^#1JOFIz6T`vbO2-S1$cUQsjkfhXq)4ZH#VG~Im?8$)>) z9v2F!rvKj5Vt0x>l-4t?Yi%K#*`sHNcHJ$PE?x}c)7a!dZG4m5+$K&e>PEfwg{|bD z!?>?CA9Js3a0I~-1z1zI@b=@(9YBsYmP<>VWky8MdbVL0<4xr2_i`*HdhIpE=D>rq z+huj@)*-8l{1nT9lc`H)X3pn34!TSY+~d@sQBIDtLC+j^l3!pIXv{uW_h1ct?Ow-- zKF|^tE>&p;CYykW|FrY5FrmhmfFpX(s4I&F@!$9yc|gw&`o0%q)V+Hq#^`IF$JS_1 zHSJw`e10|m&es8+ukS6)Bee-On?$S!5AK_XZ5pD+LLAKYkWItaxmxcoy9onY# z(4qRQ^nd*E{`3cXUCzI#UgZlce~bC}Pw#TiK#6xP@Oqc!ZyIf{cQNT}=|`oee7975 z00uIXM`1WUrllT@p!fj3k^$l>{Y_c%0aC*qh!A`&XYcz-cAPuM4>Bj-)B!NRv*s-P zrk{Sv;K0d{UfK;+TxcZZ|4a_`w^^)4a&!gwfcA1^TVInBD8rXBsBMT}usO(p#UV2A z6X|>R)v)5>A1mv+Y-imnl`B+;DM#lhAzk%KxD_f@0IbGgFP9vX?0vR+C%}fmo`xX6 ze_&9`*|Lxz#8`|f{qB_eOQC{S8ayM3KshgmQeO31FF*#_lzLTV+M8QX$6c3mpuo^{9 zS+~8EEcT-nnf-9uO5sNG(1O#nhITII?B?k^W0^g26TiFl6Mp`k2-F4m0)|b1bq}k4 z#Km$P{JV0S7jOS6MCpEQ?^mR*_ODgBncEtNVae%(?!8^hmk~B_kk}# zlw?I7p{O4Lb%(MC1CW>@`346E`$A1ds+~Am>=wE7g4)eGBDc5&x3eGlUcQlU;_tFc zyn;Hkc(tG_Tjb6JACntshhU(H72~~9FwDUGGuK+)rI4@%CMiQ`zLkMdg2r2x!w04Z zlEMw}`rvxSN^(o+z1YJ9nJ8dNXV(_|S^L>%Y}IF<;rY^p!YlCChCqXjmB8miG6Jj_ zlm#dwxm*ZnVI}L#hGaYgC>0R_EjBiye7UHg01EO+;fPy6hc(70xNX7=L4yEyy)YeY z`t*m&XFlW?d+cs~c*yS0{gD>XjwPOB>6^CkgRh2dT0QG^|8gs4tnZz3?eGgMk1agH zU%r;{HQ#waS-p{Exe!WXF-y4?bOF(h5=7XPA(m0*-GJo%+wG{hQPz8Lr<)18j0cE(FvPln z!b*%S9|hN=HPGJ=xJAN)=m}09+@jm#Kor@8?jeW71G!%vzT+Qk-Od_a*v5ii58~fO zys=>aPd~grWluTg2wKm7=a))GH)4;o{#S3X{>P^NvU=!C{JXma{HwVWmtCKkTq>r) zr2g7k!&YExBBiIDfk9Ajte|@$_bNz{^c@!FF(gOoYXqB57@WGBCIoXv*6<+AfCK^M zHbx_hKZeB9gJQI65fuXuFN#6HdNaW$XOTwo#E`Fnz)f(&02~82R1(AqTrd(VU$s|7 zb9(!+Q&_|w)q7U)tJz`HG=5cE>u=$HlgF!v?lB|%I%&fF26xGuMQdohw$Hc#eJTgM z&|+s~7>a$Jz`~C z;m7z{Keib=MJwLj2bLc_)W-_ zfN38>^&5g|;$(&FWc_PwUIf$~4#K?Xm`_tOoC?Up6WtKdS}C1fojNu%&D_2F9Tq-q zRQn9y)SlJ<;MWY{W2gGNB6j$OWp_CWY4P*#s>qk2Y&4J zO79n8wUR7$NRD{QQwWU@PDXk#5Szp*8Smi@jB`_=sNsZ5)KeJny{U7-rMLN&S6^l2 z-Ujxlp^cxhE}FOGZN}bNXo%N*<(b;kw|SZ!)IILuDdtKb<7$!F011>dakrVyP~#-sv0K^a{5w)2^D1Fb-=oz0Z)MV7Xc<* z3f~G5kqbV10M-GP$;DoI6h8Ywb> zT^=@n)vh=CZyk7DehkEP-)nP&{5JA?Yj^P5FU@kHTNp#pFY^fWD@B^;42%Z`tEhLh zE5vdtqNp{1qV|z``X9*XMX#-PyFJBTL2%Kd_iirwf9`z|75%>rgKQ`QZT#biSWI4! z4vfuyZJ#;~bDkunQ4Mq8^d$$RfT0`=6(iXG6eRB>9&A9cRHi&Au?sQfZgz2jD;MHo z2m?uDe1*hRk-~z&5X~YSkS3s;fnXp?Y;u{DhewZao%&=mL9?vth+Y2fw09UuiRfg4f(# zy=MNxx94{rn0IKw<`b=l*9>aVaOG#$VrVMZKqeZMg4R&2GN)`saB!cWn5@KkO~nte2Q|$699I=oci9{hr?q)Q(&q zxV8TqyH?GozRuLv80Vm`DYT!V=1R#BdIB~>0ha_E&j7YzR!IZKen!_w#ESi_6deop z(wD)r%5X|}uBNb(CAgx)ma3=zV*66u1c5epPY?pIhX{I65H=_3v2Z);TAecN=kmde z^YdTZKXK{A4ol$8ti6c;avWyTJ1mMtY?=AYEdD#YpxIUFAa5N1agC={J6N_=$@}@JGx}I>huv?L;W2IeDsHqm6biLsy<0T0@(qQCPr2FKMeSXJBh*p z7Ys-#aP%0BZYCSO{}e7qc!YrJ(az+DU1czc)h~t$S0cCo>ideI*%s*%NIFSM$S5k9 zBP;{VY&f7#s=*Ee0*8d%iY}(gO@O;cAq{F5 zzq*bu*+KT>Eavk=>t&1B;x2tEWPE(!$z&InQJgmm(-?b-yHw>n~k?;AP zoTsf(?1t^GALO3edIfpFfG!+W=9x}Hcgb@G;bfB-YR?#?K)3SZCOCAXHednb6bEid zFTJ#grYa7X>zEGe6(RZbYoOp^Ju@XU`yJ%71jifwQSHx)k*5lH`fd|SSyFw}V~U^B zxaHJZ%s;(iWUZjoA+_cY?>@J=@-z#H87lu?&?lp&k9yDIw{_#CIke`EnNkgt#rgcG zIRWH%G)#82Hp9h<7UCJrFh}2GpNf2F-LV8uo#1a2!;Fgrh*l8aU_BG!)A)S}XHBsq z2AaaF@D*%^uYh!haH-#J5I7b238Kx=7-%$BYUMos+huy=H4Xd$^Y_Rnky<-amQI6<8Eua9j7y*cWw1O8=I0*Fg z_z2J=ED|qhfA*dVVM$hlKQLy%Hid@?Z5Kxv<YU)uq>+eldPX&=O7xQoqQ{6)!+MB$Y-1NJcc!ID;X8Ft>1xc`P(i@RM*% zLx}5`pMSX8>5v#%2vWa?-T-qZ+Zjoei6Bd?o>V<9HYy?*%t%rKOQ2vLi%W24+NE0^ zNHK<1tfwJ@p+60}9OS-IgK8D&X^6o}TS)hh>N6Id^YX+kmu%5}cg)+(zumHlCG1_W zqfb;AOMI6fK78oZiI1OuJLjaf_SC$=mHRY*Z8uA;{$$rTSo^>JVm4U`QQsG zFZf2Gl>wE9m5R+d1??9bcsQy+pfyxpX@H@fEX95wtntQ4eG91G;y1SpE7M^*4KVvsY^%x^cLSG=g$Bh-{oRWnIFH?f7$YW%lRKD>-WY^Oi_d&5`~h&)2xZ`%*vDA_+i8Y=%MPY-tXb}SZ0K;wHC zN1I+jn?{?yhaS*#Hxe^N%!&?)XtWAn6O0Bi7bf?i#*tbe4n=$Mx6lv(6@yz78VUI? zWfUJ{GV)qMfPtrmCs`BXRXQd;8ZOHC*wdl?)`ABUmVh$QG`jcrVSOe)%kR8=2pcQ% z+^lb=s!g&6t$6mCMf}yJ@qZlpgDL&@PQvrN$Iy*`Azv)!mWS9l07wSIP>F>{t+6K2s-z(eGvxj4+oj)}u8x8Id-GeHHxIqEt0aI#w|ck?T`#$a4?%B2g7W zxSdyuBv!GMi>|`#KrZ(wx>^skd!9kwXyB-@d?lHDi$cyDSbOqHCnBT3=1~2q-kVq` z#3rx%WoiAuaS`UOQpQeIUy!r$L&ksa-MK}NDCArXotHMMvNb)bLYlc1FKC(d=_Re1 z{Pg_sO>4Uf4VN};3-I~NXsQN$>8INELSMwc0;Os~J|q@nAW4kF?c|XQd9>WKY69m7 zONv5UU`o-Ui0Qh!OuNa-pFQsAl@yT{+H+7oUvcf8kIyZmGVEz!lG|z9rNwwpfb^Jn zPZdHr0+1!xz6Ak1UPqfp%0mDE%v}ui0x(Lb>Fh!8kt&alhma!zrco%exXK?1tr4Ew zL)(7u8XZPg72_?!{b4N*mA_Z+DL>L$a+X6M@YzFhqG>ZK_grI@AF1#d-TRrmS#AI; z&6jeV@B!dA^vqmZ;^0Ife7MaEUUP!wb?uUy+ofxs@s9GN zkPOB{=&8mpV4PbZqmh&E?h=$7BfRVsxBv%diS{D3RB-E=QD39DfhAL#OEybRET#(b zZjl#To~IoW+99}%J?+4NijsDv8lh7e2NTDi2{fQy?SOt*^6g4pfJMB<0cDELVvn^# z0^`=gwOIR=#o9-b5hjby5A3g^Gb|4?EN3OuDB54fw4$}{z?`^RU1|~J@Cm40ap3IU zd~sieEj9W-Jqs%wcn~!Ya+mUcqNNc|UmwUUUviUs47C^xt|)~E$u&TTBH9r!3KV$c zv1II?@8N|(RG+@V(TW96ky~*Ms^mpPgoNlA52}?HY)ecGqAGc?8`4lIkED^JtM$yT zK)zC?ybi1p;WonRfALh+4&~x3740<}m*aW-;5e^hdBAEY&5mpc@CD!9qJ)D(9l%2#wKP(&lg*qZB*W!-XWAW9f`R ztdRt6nU|4f?YRO^5rcv!xMQVwm>w$)kHP^F6%!RhZ2g{?DaGRdA!Z7N^C59l{)V#R zVX;$ribvP{5p!J&wxplb9(W(!hd{OE!!T=5CzS!9P+=^+Ex}Q!@_g|Z^a<;we@!95 z0Ng<}k;TFmNG_yJVRbW7Mdb=}*&l47Hdh|W`*Ht?d_O=FE95}}G%;42ixx1d?`;|n z|3JKSPiK*_Q9`giGB^-Sx~Nib!S9N|sBq z4O|h3?&P5Z216RG0}p#q@%MnO8PoKuFthqKG&XTq6F|Aqg;>$`1vPxh>zoeU3$xj1 zm{RIm@lO3|**KJG(Bh@|gqkQ%W5S@>GZZ?JP3g1&z{ZU11#jmaBC3paS}afQSGB5R zK*fuf<=0vDcP0+9)eN%_O1bo{_UvJ0$do+aJC*>R{|&FAtjl*DoSbJg-tqNg{^!|7 z7wPcDa}GSHKIv#i@od9ehPg$ZJA7I+qoTy-f16Q{jJ^k1LcDXpq?n+)KvGdjWb(Ko z3$0e*A2P9w#w}_W73Q8HynapWszF#F+-S%^N%J}z)`Sl29oQk202fNJ_H{_uFk|D z;HY$eM=o%{i0}vtT4gj72OxH!9XNZY4CZTP9*pBQL zesbgNXO=Z-{NmJ~wGj4q>^YXPyVkcX@AMLWXZMQz%WluEHR19iwEZnLLblrTd4$b4A`KVsr;6209I-FUNhNId}^)ODCf5^Qepi@!q6#)LO z$gwPOzu_q3obn~&5cl6>ZU&@Bb?F^vV8x>Pn>J*NAWxfsLn%cuPFh^BL?JZDMI*vK z#m5lJq+r7npG;){#;_itf4eWH2dILFzKnbR!}GDbttlxf)l;ek+Z=XB?K*K(>eFVA zGng<^1{?e&5bU8L#0LaM3sL4X$qru%pfO1)46ty$$mbL`pCz)cY{pY8g)QedK4|vg z$x%B`$+v%Ox@@D&mz_Mjd^6)m`DhmY%MH}{+{q`Z(uI9vw%6>!^JjkX%)-UJK5B7q z@%DYAhkbu_WbQjky`s{dnAa7BSJl4e6zGH#bzf1KJ(^?`=%#`LWNcA5%i%mG!CJ-_ z09+u>L_TLi4hfqDZZMAo6@?9>yuJ|5T({W7mxxr<3W-l7lNKp^2&vU&HpC{9lIhe) zHl`;EX8`hRgf8%RTgD^nSFfg4X+QV%Cj1)e4z7_S(?NA>uG-l@B}j=HlCWrPWz*uj z+`~ZF>lIWx#EnAr%wI57ZP&*H2V#QCe0)#&oZ1eOd#$RXCh30Sl>A4u*=;Z z3rKaDA@Kwk1R+J8m_BV*yC{oba3sTfM`eMWs0lTL9bC?j-%^%uYmN4BWh_VppW#C; z@>fphFTed90^0_9-(s~Q8xOwhN9R#U6}T=pOO zXhehXj3MqWqUi+ez6#t@02q6GNT&t9PpF)vjRoOXao$23b0duda1Vqy3|&1^w>v>M zCQ;D7Uv*qaVYA$e7L4JSwO`m`p@r<>aRT*CR5@g#y^M82r zN2U>fBq%fX^Pl#-8Wgy85C288$}jM7_3E(~G{Tz5(ul$&40GbaB-T`Jf(CY&{veKfK}U6x;-Fq|qq+k|HPrE!nL)3oIE$;q@yC>MHju;|7&N%K#iD zq@egAnE0q10nil?$;4hvP*jQ`7hx)__RGv+m;@vRO3O85`}n?doA|G}7bl$Bb>+%F zU??;{jw*XxKqq2Ra!&h{f4QM=*JBee9@u+|FcSp?a055CYQA5%-}ep=S` zV~Q!rND-ThfJjnrS|%OJ*jWl}wnRv)W-!zmK}J-Y-@y$|%~n{0*qeVpR? zgl#H*KE8ItoTPUD@?8G=2KV++Tf8^4fQ>XK_mQXyGu+pgd0hNu~4J$-s zu&^%pQOwv@GKbESm*C}e@Xn;+%UrEwRC>wG?9#1WW{x(iOIC+&-Ll$uHRW8mz@pt8 zGEI@&)2LC`uIPuz?fDbAJxl&a?NPk*jl6#6FS=Uz_vp|*CmV@R@|YZv-h*Vmqvy}F z6w2Xq_U_rRX^$RFMJ}K0s)aed@jsu_P|WE7{szKJi6!PV|6BD>zDndVnP}uab1L{G zFy<7K_t3YOo>&@rm~3>w<$tg2(+=?G**F%Y{)w)%A|iEKXs_`X4B3S}FqV%RZ#QBY z8nRujUnT!Q{&&1OUs?!d>ObAjm%3VObieciYI8Qg{!fc6uzS{lGT#_{^nB^^m?xhcs75pTA_{&&Ya_=kKoSpp3ARr+N+LRl2)RRT!Cp8C-?DHLSni(xdtoKyPacw5 zLFp64po!AC6_^6dA2yls9_GMbsJKPNd_i(Gh zjP!SG!?apaa)zDMh{f6`@H=e?8oR7xk`Z*e{5$?L5`WV6u?!Z&V%PGgT(|g(m|U~r zqTCkyLy^E)My>>5DAW+-@WO;KW0%8>Wj&r?%{mz_DoL)3)aLctUU_gK)iv`Wf~GEh*q{ydglrnfv-cv&q6yD<REQ^3|UfXnXf+^5Wfh@6wzY^>)I?thX}- zTvqdR=9g-vW5me4Wu;o_QIT*;8lYIgISB)dK2u732HT4+j)C$e=L**TQ6kta#%ToU z$pXU(Z!Fk-X}INFXwhX{0~9zFY$;zZEFnAr?V!5pRA!Pv0ExQk0KK?_-eIVlj&(yd z(?MbjEi_pDi@(3NeWCRJC4O%CGM4)D{LAfFZOz=EXHsIH#xJa>^R_D8J*e*K=z3@H z+!L(Ru^Qj8zNhnAt<1k_FQ;s_CocQ_g&N;+T12w6r|^n-yBLd9sfp9+3$GshMQ&dj zRRX{PgJnX@Ac$_|(e8sK8IBrQ6`%sg#)PGYr-Jzh#duh#j@~f@n?eBFiBn`dYP1k^ zhs7C?f+G$W5deZ}NsvuB!LXC{VIP0V>dm;Yn(vx8k-xqA!i;)fvX5XwxZVD z8oiZ;Z=gNGq@(CxJIF1&lw6_~d^|)J+?d$Hk5~&{KNRS1=_pHF(s#l-mat&-;595^ zQNh~jXBsvC`uS`8glto8x@^luh-%~@7TeANneD%DAHv3kS_yMgw-RpmG(JpwV97^ zGniL67>r7rx~Q6o+V9n{YwAn>K9Y~Wgrp33j#c9nQS;p&HQzy>=a(z_`6ahS{dTg* z$jpw%&casAr4>isBPVLdKjeN~PB*H0IXm~GO5Bb5@71bGQp?Ak4XFHk)h5-Npv9?F z`#lsz;Pv|NHNr%lci|Bc^Bl>7q_mP{;oXb4W|A!})T{b?1Gr46`a9flqWHUU-soBZtv^RAnO3zwlZ_ssvTS& z-Q(A0eGES^HiO^)n6LYU-+!)O-@g6eg-T|xi^f3u-uPXq(!M?f%@{^kLm-tug5Uay{CxiHLK|Nf=O}5;DZ> zBwyyMh}^^CDqrk5cRGj7E005#mSf=tx%C^jN)t|rB8slk;9ca!c55>*lL%@HZN~d^ zq`x8{Tsk?Ne9VfM_&?wU?ku60==~8`9muOVia_EGFb1%qpiNNrh%5`2m>_R4=0g4g zW|fQ-8+QdMkrv{O1q+s_U{4WdXBtpBZ_b*%qF#d+=d5@U05%^~A`iRFsw{^Qc<$m( zesdkWHe$)5AD(Lb?DggQSFj+~^dx`ni^eD&dSc!UG!OxI$QxhNIkc~WbZ$3nc%iJ4 z<1+~Xab(PnrnA`G3rUo5ZH+jEvCzqKRPsnwt$<*jeuO9MS4&g={qi_GJjHW4JrZ6- zHp%P0lFx@DK{P~~Qgp3^ENPRE*#y6cS4${f5=2>+(35QmfHDM$bY;6$aHW$Z50c1x zDE3N`4vi`w;A%zUyRrosv?-&8Jc}|aAOAk>OylNX&%Dl0FxGTz-w6xZW4wdmNdxEx zjHg{qgrI!S)r8;RKljPYgQR3(3jR%k)EKpC(^iZvlRBCE8V7;1?$PZm!bKYg<=?R6 zaZc+;&d)H*h3P)6@C=g&$v_bq89qtS;9zU8F2VyLfPD}1?U5IP9(}w(HUP+Jz6djY z%df0w6aVna~^6!|W!Z74lhInGl5VA7x3k*MqeH49wse@&o z8IKu>Ga^bO(v)$e%EC+K5CrD|$b8DbQ)A?=)yuM0Z+|b#*uZ_bg@jnNqGm+P zCRtfD9!$E72Znj;*6_sBX-Tmtp(BIGiL??qj33vFv5NchzgGA0!|=6*NvXm+>UfkI zZla~dY|yCif5Jyr({9VbRoPsnh09vQwKr}z9D=Rm_PS~)x6&}j%!Yx#Z@Pn?xa|@& z)C0;FV@{kI9ns|I9Zi zD_p~s6?&^(cTb(tdW*ofEKP!3{1JLs2rQ0f&c;3{IZts(STWE9d<40G0ofu3L%P?w zkEmcM06d`;Ln|gaP-r6xw}&8dYxSk_@u7||Do?LxZ z!;z6V@!SlSu=#ABp?foy(5UYcz8$2s-|%(3AAkW>_x&-|X0+ztiP?1Z*&S@&#)*8~ zw}<&Fx1zNf^3$Gn3ES)emH}t}uC%!l^-~|N|J7-H0*!`t;k%IHn20S#Sx6A2?MZcY3 zQs;V0N6*D%=)ahyk4AlzJ-9oIoqVw9H+;Fn*j#QRo*(SQvk`B_Yw_qHhGky-HF9%Fexy%nUgo?Wwh_s;IxQy$W@OP9R7E?s(hezJ;3*!%4L zBfJTF?+92I3KWv`WH%B6Bu%n$FcF;udx?_sM>IRR>!epJ7&Q-DNEmLW7Im(DdSNA z1zKe}IQ0o(4Xr?^u&{e`A+>>Dk)+yEy97s-N{*TdHItE?2s{F_L%<|SvNgS?+hTVU zcaUm?EeW%rIRC(n3NRk#1sj*2;HNh0jDI!OFX7h?FTRMka|2rJM39$K$MhkErpbTH<>I%e!^uZM`TprB6Qd^1OESGunt}vs-MIA^3mi+3Z${XZJz7CyREw zalTlbXgQ$&;TIhiB?(8-h_1n#R z`I#q|;z@nb_Q|5{|4Ywjx4fV4YNrj>1{co%pNx$**fZ|hVDGWWD7!G zqrR@aIJIp4f>R6FD*6wnw&y>#RIU>)*J16nSiV||wM^&74VCey3TfZ>C#uigL)6^+ z56LeqZOdT1jA{%aK2}so71S5V4MDTO7cVUHfB=*mMs=Z}Kw2Uws!Be`l0{XiArCLC zYKfqBlrFAH?VpzZxaIj<3+a<=zw<(7|me)MphnA<5{sTT#hWS&ME_)grnd zV{96R`$f*GKDvY&)Ae`^DlVd&Rhyo(st;bN0V^IPj0@Uh%Ea@0Fkgf`K|siz%7dqZ z@j~d_OjOtaz{&^wAn=6%%wUg0se~X9+Sq{s2$E8m@}xFM-imCLZC8KcEwuhDll5fX z`Rh=kUNTF6am0#Y8q~at%<9=}C>zXXpsC{hK7H|i1#3ni{~&SOKqr7GgmO(zE+j<@ z)!o;CL}w7allD)Ht7rr2kLx`o%S+i(Vp-@*z_T3iu1BjK45*OXLo%YMb}&^-v;Tjq z9W2JgqKPpP^{|5cEYRaHEGASA6vKkNrs8q28mxiDlBJJ~*?VLtr0bwKml&Uu4=)8w ztF!cq#gOUiwWRIqv3T%pu^m?0_El(|`W+POlw?7Mv!=dQf!RmaGxYWTZABm!D^?c* z4{m>-GPZxImF(I2#Vgvg?a?}!+F&{AZUb-*dJEuKMQ%IZPrhxuUL?}{nmtMMHdCqB zmhsAxuhYi~8}DItn#lqcufmn?I?WG=l!uFxGABmJ5pyB>7AV=B<^3r06LJ>9mLwSj z0tpkF&1w_5S$|m?xk1i|IzvrG8%Hav}{&LHv8q`JtUh$ zsS5dRg+1u_9Mh6xJ}CJ`MR3ggXILQo6RJnCuDviH3H;iF5!ni}@tC>O0H zu^)~8?s0fTze34w7k3#;fJsveuPAfC#S|nBaQdRqg~Sj&lqipZe4tirb+O50`1r*{ z!rd8(FUU3R{*2V@;;)^d^bJgC%1g(#%!L#uVvOuP05rrCja0aepi0EL;6^P>WzCvA z^-?lx%8!~Kdwq1dRu)#(uV+Vf+}(q(Wei}KQ(#rNY|tc4eAJ# zHPMe?2XO+}M2%DGheNnjyT>-IFufKFI9;*w)DBIEEW$r%nEYG8t~FUb^*%hOZ8j|h zj?hN+w;1ecTO#~7fJq0*$%q*w(O=SY`0Ie&igjAR&!^UQ4Vj`GX*0&vVv4eozim3Q z?tLDq&2#-a0sWUkd*zjb+JixPH(p4XLpo^EJ1Bw4+DU(QXgrknck6n z8g~lcm*@xBNZ}t4S3+PD*s*w$9=JH$>8MiK9!GUGD`!+ex$HPpLn=9M{*N#Y%*#Zx zd;R`M^Qm<$9m@a588%$;1o1sT^*JAn800PEuTqMXCN+09NlQ+Oi$#qDSeccOMa2OB z#J+=nf%1j)nv;|3)5Rf4uJ~}dxieINNQ0)*2g%mEzJr`AbHib~;v{(-SsHHN> zn@-01HR_S=;B@&IlUa`@tYS#T4sB{jSFDs<^Qjje539Y0pWOQmzcNdCzv2h;rXFa# zt0fDU2Wov9%pJGs9pkTs(#g7$_VqJ-8d|RZ>-`Z9t@A72wEqLv=KK?%Ojz2#^Q(h* zXv&O)u4@Od2q29hyYk=S#=^#h9i<45-x0A^@>oFZfS?I+B>Yn#pBVNg{*{S5JgV~O zsRPd(A`3NC6q*bt&k24-G+))4ztp2+#~v@evuu6$?(3JmBmTvW9lLbtxcuG0gWp|_ z-yj>CfJ_^(NU=oVOcj7XlJTBvkY-gonuH%2^Hx3e75m{pY&TssPe8$DpIYdF@4X-NA zVN9B46X;iO6pcsU5@QiP*B6ze))7LP^*`i>!IqB`~7N2dC;C#2)Nk*SSz z5NoQDPE!#MFBh7CA_p-0uxgpoTLR1&rA3LIhf(Hc$uF|^z%>tA?Wjr_Zr$oQa+e1( z-}{qM@s3T?rs8}lSff3meOI^;?JVXLMv^&HR7KBd=z`NAZ)ubIE8V>v(U8k zf;Dm@KH!%2gj^R;xMjcoie^2n-7v0zFEUx`)OtHbcFU9IfX7cE^EDU!5PnEQF0vX8 z8AP%Ha#Z&PXa`c;`xRa2Nmfdh5Cl)PsuVP_LRCWk3j~-OVC3PNbh{?Qjl{HOP&Rfz z5z#hv`No-7=PhGZe`Z0fwF#wp*3F%_s$R~^xoqgbfd97qzzY{AWIw;PZ_L2t7ndA7B<8qakv3GjUN{9K=UoF9D%1Z0*E&U@90+$6 zEF!XXzV)0=woYrKV21Xz_D$gyytl|dgbBl%z``WD6{s}u1`y=~E+-Fzwa9md$Kt;< ztLq-0$HrXIp5FHPTiR(na|52~D)CILCpfOXG(38yRMs=CX`w{kQn2AN{L*u@r{!vI zeZCFP%-2@)j)jZSgpRa_^cuMUvZ4+dWCT;aO=HTC+Lx*K)4C)R}hap1p7!Wr~Hi!Yb4ZDXzrt#!8u#2di-~~#cMHdBl z4YUhGpVHS+W@<<&yVGhl>nT0LVq*Avj4_D&@^9pp{DOfqnb?pJNdH}w7Ge^hE5?`5 z9@GtUI)H}-8XpFra10FcvNsV60G&8_Ig9DU8Qc6YkZW1ER$1x_PX!hb zv7B7!8$(2faMDyCR%i3y1b z4r`DvsAb7KPA|p%Ou-3};!z(yEBpggTj^;=Hw-$1bzn^p^^W+nTF+F!TJ1)I75a~= zJu6w#GRID?KE3%5n_Mn=IzQN=Yf_g4Q*FLt;XP&`V&rEh-0uOVXq`X0sc-Q8&y}>B zH(e)&WdU&_OS{0O*dJK21gWZo`nQSk=o~62V#tM#2Er>QPr;Zexfop(nTI};iv^e_ zwM(h!2uU_llCw=k4vwydc|yDZT_p*)Byx~IB#fF$I?F~3Q^VA3|Gt@P`H%Y!@o(3& zIy+X*-|#8_YCQ}6_7XGMGkNo_9b5Nc+YC*3L9G9_qx^@J>sZ|TC{LKX_3(jr_BcLc z8HZwTq3qXH&f|=M3j1#IsJ>4oY`#Zl&N$e*KO%oBURvP{h{h>gju}+s?iaB>6Tcb- z^NcvVv2hW=Gy&g+Wdeyuiw9IfZn#ez1;Vk+1m8oM*Aa1vehEDUkAtj7T&3tiH1)Jw zrFaK`?cfBpAgu==74LLreVWq+ZB2M6X?!tE`H=sa)7a4{Bx(5L>o2-olRtkpyH!f% zoXN)1n7NVdeUt+K;FGtN#y7ux@q_HpFa5er_!fP~v8@g_)r9Q{TqRuGI#;9l|Iqdw zfKgOk+jD1jXLr++O;1SM6apcYBoJCCA@mvofdEOUL25vx8|f$@O{Ix|(vd3NLI(xK z0t(7&MMYl}UmK8QZ~o`p*-bVfsNehjpBl5-ot?Sm+(br8np%^Do(|p?q}tuSmEVU&vN7KR$K@TgC74vuyQ9T|e$| zXWt#(L5{>P_RHS1!zyTHu@XA5W(=rh}u6zY5Un!L>9sK_wDOEd_6!`R4#sDv;}m?a`ys~oF2 z_#FWGUT{*@#HlR_j4F_RZ7s6)-xwa`T6I#7OKDDPfzb(+QZti{ zKzB3MfV~R~4a;#jq;s@p2Scr7WA)wq{QLs_0?ih(7d(Xg)Ctz1OmM5YF2K$KP1Uar z+hHHt$ROm3K;xmx2dMi+Z35et$V^AW7?Ou&fG4Qv7vvnFLR$SrqMg0|u2}$L%8+F< znpN@Dh5fU8jvL;#X*N5(e|G!)>`tla?N{1LejQRbpKjA8x0TB)C+atj?Zf7Xf?pUb zdwy4Y_~^n>9f4w(l;4G|3BgIMXPgNgfq=%T2T%oKg*YNWb^{rw2pk#XxQ8Sn_7PDL z7NZKPHo@*F>_ornQB)q00%kq_44XZy)8L#I4FM3HHN0g}K}LgCV~`Wptv|BCC?~8( z&jF~Lm1j5}HSGE1J(63$V0eNky)VNaihPsCzFrBycWx`>! zQUTgVt7BH5nk`R2Pr${dV8=l~9wAvYAAKSa0!(CVRT8{bF}PHo|Myoy+aRa{*_alS ziUS9McpEs@r@HHo@C3)~H|uwoUz;;v%#bdPJH5Ssc9#j`v(vg%{4}V0xBR@Gy#`dL zpBdxb*Pdfr+1X zNl=cvG6`iFu|JV-juiG{`T+YdlY3fHBLyeDhEO9=g^NO7MKOtS3Z$#N)2l(>9+?aO z)=O+18#;C58ozzszZ}@W=kYP%AlOzq^f`5BqeaJqVq${2kq~`@G#m! zMD~QjY8i7FXui54TfPZ-8ZP9i+0&&%g*auJ>fN}(UliigIZ=pD8_~WuAuk6_8z3(M zQi+fjWLLwlf1xPKp$5GSdQNjWUgbQ_& zSfus)D$!sU?Xq#OBD-1?o`07_VegF_H20s$i9qMQ_J4~>loRW~B(e&N>SP^Y{0$>$ zt2BdJACN5Jdcp|S8Ky_Nv1g!zsUgZX#Ka>tA}vM38u-B&^HnP>Y4Ob8!#i@+Ed@EDB47is~2$JiYD{2Q7bL9Ta{H^XTN za~M}lz+6c%fUw0D;Dyu!T8JO60TY4k8qjb&e2G6U0a8a+U^`27UO}$>zr4v`eIIyZ zn+*;?n^=f2>CE9VDf}I)%RMIFW4{|*cC!vZ-!W+ymT)0()_Oa(h2ESG=Q@^jU%PK! z<3X&7I*WS>8$>CDmAce$^?scC0o3^#_p@<4(XGJYNyA0n0~#o#Bx8K*yq&cZE1}yV zXB$XOhx;L8Rro`q$-tBpj5$6;20H#kVj`Uybr_wZjZhgxE43Tz^Wn&&H2435dm|%O z)(`x@xwle`U5>;O?Os!PTYm<%yCbBjhm6STqfLe^qeh6usVD%C%n*Z7HO#{PX9N-z zx-ce_LO;-$L5WI=wBSu`So?YzSL!gR_AfeIO=nXCsl)K?n8+AHsdpM)1r+jWQWbe> zl$O{byr6O}zm@f!?wYk#Orux&SXtl<{_AH;`R$`DLw@EP+e0%r- z+7~FZ4Krc;0(5hIDgLO@BEiQ+&4S7!c=t%z=g5<{Sfxbu0h;_}m?ZfkFJH%N>1-ci+2o zjqe6r2v~S>4CaW zo@ktp_4ikG(Q(c~*;O9&WU`DL%l{J0rpii5gBeKR{Xv0vlTNjli;^a6l}Jz?nl10MJ^*#Nqf!w~*Bs zo}30VshynKr;wT94gjmig2gjtzqlIN8D=_-t7j(_Rva7KlhhCFr-+9- z{Rlfw!wpg#Y{5gd=uq3lO$5N4;cg*o&V`^&#a$5tK*b50Jr{-##dQ?^s_T-x%((Ed zU|VfEE#ws(8XOu@&k=u!c93JqvxZ~op*nT7O}c+jx6aYF`}-Ppb=h<}vUzo%I@r9Z z=uT`c^_*1`o!x~1^26ALmWHu$s?IMD!u#S99N)#YKF!MnE0Sj4KEkGT`3E-bBW&2e zi++$)tEhGA1W9nn$w9mae^*t7?F$dPLPvojKgy{NkE+PY(L0q@=yhckP<7XR@(J!L zmnB=9SEg1jalHTkBIl>fs#nUaJnDxZ*vKD#pmjgUI?Q;YvH(0fgjaD!6?3Zp&ZI)} z){c=7V1(WdB2-GXV_|VXz5D9I!y?mBv_tna|Cx{DUly-l%#N_GtUJ51bl#KJAFacN z?i>F^|ECt@Jn`c9G)9Nvr`!qOnFmf28%u->%u^}Y)0G@(NLQvmW&5ADY9wC5HU<2T zXt~iOkqqNYyb;*fHM6zq13_#S=#M9O1X%Ql5iuXKg8+r|@jzAxp+xgL{BYQvK`;W1#4?bY64ow*K#N4xdJrCla zvnK5LzZ*B-%Cp#odxh^a_28Z(8)xsVcxC~=e`*Ei->LBe~Y!hU_EQN z@b2pwsSgGirCuq`p5r%>2PaF@;p<(GwlzW~MC;6!F-GXUp}8V?5+A;gK{4WB3_$xoadUo!D!CaqjHZTb?_cE1^VlfUxzk{Q#UUB#qL6DLeK zxgqrIojYek&;H2RkGebcr_X%+Nsu?`r+mMA>nF{--P?YZf4yuO3;uBDgKo_~*}D7t zDf&t&_{qm-POr~@I`R7J`}dzbP5=~{@K5S0#1p9B#hEVA2J}0`UG!Vqse@{EAM9Gy z-A6Ew{9g>!AM8Wq@G01T6XTg}e4--#eN0BF5sMS!BNFPPV^Twq?p^2TK;0$IWsFXW z!|-4&DKAewQ!-=R2J6ZdGiENo$hUtQdV_DfxP0dHl`E|q#!sDi`ic6X7k>WvLg@Ls zcXgjOeYSMb%7(w`=9L}VbE|ddd#_&Ox0WnnK_BhC-?{azJ%`HX>3(ata#88CO}X{0 zW5?b)dE&^Mw6m`Q$ISxZ%q(!{x0Rl?@r~9ClnqLY=8fWXs=*l|q7v_dxz@XIQ`wns zLLsrkQL4nQ8c#s*<)&1`l8`@LGPernrUC8b6^tyh7 zFR;O12j5|XPUrRjP0E;_xu^N&JHgN1UN~le{6fEx3xC)fyz+;IBl}rL<&Rl-Ta2&u ztZ4!EC2f4yXj-Y+>>(SNZG0mzM%wt&yK>EUZOj&7G_ZwX@(frFfIW_@;;ZY2#~y*IDTe3;zr}Z0|@1gHl<8>3h z-of|C&&>OH>igrbFWow4JJYRtm+!v({;T|>t1*1C{ERpkPN02#Aq&0|>I;})Lp#=s zu$}s%P^Z8zA`*gc`AWW#;l83+gc)p$0KVJHM9>~e8|n!tCnlUuO-_HtPb^yfBx6r3 zp1)Xm6O3>f{3`$M62BeEo66JoKey_QLodP&{}n zs6aiv2+to*z}9d??5w|A@AGHn&sayYVarx5ECXHu>pCep~1nkFcSBu zA|9v%Zb6Y4f(Af4wZ24!cgiIBeT|D*<7j^!_jCjLE=&T?5BN@~=1eu)j|hOn5F7U^tPI0_1@0#oO{s#IO^0FI|h0LKVCj zJ2!6Dta11~V%D7R|t&l`%aAqNz5xeIM{rZ^kk z5kM0`0`15e?3h_f!6o~@pf_K`BKb}02>ux?j;G`&(f?W2*|?#0Z^QGLA6LS#Lw%&E zB1-vH=?7E=2rN}t`5J|mHf5AtM5NP{#foR4>YnyEA7*&IyoHij{vRqSr(aqD&la;`A2j<4uJi_;kR>vr*sNx<$Ag)2E9o*$4GM_#7hJ=7dY8K5sM^IM9L8s zn&k|umkR1(&I$e<1cQg(G3e*vVldBYS4#72pA-7Y6&(w34Zu`^S``^DU{(eRyq60g z!ypPx!GDUynOT$|x%xHhJaXL|8;AdN`^+ml&fNaR@ed`E`?4SDr{bn8Te%Xc+lSf{ zKXGq%2k$Uzr4orTZN}b_&4v$wD@Mi%QF(MUL@L~C?qqhvEFBN^SB1{{eu>fN5q3%lu-zV-yUB^>|?~|OB z@ap2?x}23CRvcn&Xl`_0R#@nMTX4VqxSs<)1~)?*vw}~l;u48JwfHYwmHT;9loiO5 z|3rqoo9;^v^;5cHeM|RDWoJ0^Y`l6{K>%{l@)!6khK&>-8oa~~K;BusUpkV>Ei9FN z@$Mz&&F7rw3l3es?!rr1sS7TarMJ{px?ExZ{xA3&5Ed7A zvbrhbD>tE=>Oq9qk`V^SqxA+qluoiH$ya!%%uIIJio%_!)3|lDn;SOG+TT;@d*;+A zfOPTQP3%zX*1WTP#pLT9aj9*?2$>WTPG#9ZY|m> zyR#e}ss&(gwqgIM1ArwlF~Pt1&eofy;_p$+K5J+IM81)zo zHDpD=o!DSOb|5_xPDeMTVg>Aebx{I+AG*7E0%KYFo=UkP1o!`^T-dmq9P_n2tf#)G zb`NQZb*FAlWdp>>@~DNc!HKVQ(u!XP)DaXz`$Y(qDx1sm>$m{hAnLUr5kW9}jUm0C zZyj+{<@iOme@_0`!Y*kYFCUoOrDR-oTGx*~)}C(JqGwCf+IL#E>WPQ+kzLu(#uj4k zZIMrrQw^U$F=oSF`vA%ulX|)$sIB2bvC)D9h$>i#;D`f8p$1+d+Qtbq!1h~qwUYzs zHrZbZS&&w#(egR?y!BEy8YdeT6Wa;oZvvWuNyUYpSSrN{ z*ef5g-_?OPm=gzEGQPITIz>KLxvEzG@QHw$Vxy$H$_1iZgf*q_Ac|D$gF2pP1~@O= zXVurNJDw2CkKnZYi|0}M$7AitVmQU~Bw0KU<2vu559)dzRmSsN#k$_IPWgy+wBFL5 zx81s2S6n$4E71|7NvNJ$Pb9&{1O3EDH6AHGsSANfJ|v-#En)Y*t!OoazXw_sSN0qA z^SE`2uD=-99@TOKd?N;Kyf&MTCmWti=TRMJUL8Fp5LAk%u75?rEuL{2W4jg4d(OID zcR{+1eR&8yk1*GO&}(5Cg4`;yEmSH_L4^)Og^pEJYY9Ru6^=+P2>S5HTF=4Uk17iG z6m0OtYN=bg3)bc!Exyn5*(=P*Pv=|&&S@jt$j>5fdqZ8y*BG}}kG&IQ*`Ed&qkzR@ zpKbU-B^kmj#$HrRsQF1MT09Cr6`O9!i!_FDS>rk{f5+OWSL}WmIAlOrYRE5lR2@3Q^Zb&ClQUOC)=O=PU*u(V2z&dtr z;4!ql9b!`V-VRx!%{>#oL4O|)Hy46!*y;q@cG(z1Ogecn4K}iUP@p6OZW06FjJ1D* z)!Ma8QE}$krD(1o2M5zXP z72uyl^bVT0zVzYmVCw4l3PoD0P(TPbiLTa-1R{gqMp}Y6Y}IS);me1B1S;&D+J%rn zlg4MKcjUngR3t-+`t>Pbc-60{s9#Q@vCM1rc>q6Jxvsv9U)-(j(8BiZ2Mto{w<{{3 z7c?Gn*%jbg&Y8S09u1|=G#Z(ie!jA)6G&tXs>x6+f%IOLoB)B+qg**MX7DAP>x8>u zMR3IFS+555qa(cl=@N)I0@FUaSDtnD)%t)SEMp5hrHMY3Oz4=>#r96)Zq!S>lh)Dj zFPACnl9Do$T_(PikkFD1_n5pUF`-4W>G6$;NtqSjtx(LX3EcLu>pSnG z^DYF9rHv9jfUO2}2V9DT?$8&YXL{PO=mG|cbxbLc9W_5jsXD6o;w5UWi~d=_319&5 z0;v}1;T0W^!(Se8{8M@Hr^i?{|Mb{R>o}_s+3>NSf6hPQAAXJYl~1!ea~bD`QK~0K z+RJDFLysaGfVqO!ENFDJdwUvEP0T08>b&MY2;$n^vpTbRpT5Qd`FF1Y#e;iQphl2& zj?sd?+O=5Hw*E0khadRqtaGO9*fFJfH^#a(SM{qNpZwBOxhV>Ou@ehlm*~4N8Th<9?Fv5cFYJo)kMOQNY?G1>2y@JI9mk#_Qh;~UG@xv$=flQ>pDig_mcS8OOTdC}_{V}Y#+G=vJ zWu9@gpDMcQ?^%ogJKmF8+#X%)B0iz9lqRbk*)N7Ym3I8`rJt`0;AT;*On)i#v6M!dIUz$7}3K4m+_%{~qOF z4C=L#_QDldw)fyu+0z;{nATy={)10-z^kcvC6Dcwm6ey5mDP{O=4NO0VXRM9`@Vec z)a~gB3F$i@f8v$Y`1sV9CpR5DuvzOtgId$?mW72Ft2fnt)*p;_aM1LkerS6d>O}~h znp80;3N>@Jh%pND0*{J9%6!caZ6Gq6bxS*$Lfcrkqd6>n#nC>1#@+da=l_CX#Ix^N zk0|>q$0Iwxz8J%LnD9DEw-fuN-CowGs4gs=;$TnHk+e>b7 zSX6TPbae!8WSWd9dbBi4%(zoDaPhdgf&7n@`-CJ$XxvH(lsHsh5#}>c^Ux9u@Mq@% zmL(NJ!-IiD1+K-VfYkrpwXjPp5xx;9xl+CC+?Yk|Bh3)Kv_xnKUPP3E%dtcgR3sI~7>ahNek~5eG>fMSoj@lvehFl*21Y${%6vwbmn@AA$lXIPmO< zKmyRtq@Vy_A9q(5XQL{4GjB?bw(Wl)JEP3udMsSfF93;*nrw6`R{VV6%*EB)cXAK+ zy;=Q3hDIzydG+M%9&^=#t1ku}yK?oF4u=zBhbI=W51iZOqKy@!)k)S)PK)tCcPT}* zkwEI$s}w+0Xe);Gh2NSD4|lx_8be+sy?y(H=@H`qthcs2<-wYzrZ&w;ZQRT>+G>>V zAB}6?Jg!0W=8$K(d=$o{EylzZV`8fajgi1iD#gS?hb#pAlBGf@1hUlA-Hpn9y9K!i z;jymF)xlX}8v_F{y5RQ5UqYw%31Jl7E877}JGUL>Q&32Sv7YGvZjU`5U*GMq zeQcCz!l$>yGN0Fm!t- zVIlIfh;zk&kt5K3iW{r=?AV@tM^67 z2-P3X8*N1Zpg?`bFLYW#g%#0ji^InX4GE+a12ICoOdA_S>8Un?ueV2#CsMFQr>P!9 z?Umz4^ArQQN%thFLG;tNV*S$XTb9r2d}fDhmx6JL@e{iB?4cHYw#9w-CtcVx<)=Ch zcySo>Dk>czq=rFeCh-9D+0%ahldxbNBHlomz)VN3Hk@ZN#G3Gk5GItS#(SQUrekpPc&anI_pI>z{Hu$Vc~C_bTtej`r%;!}_zX738OEgF2~F zhWsFICRS`bY92_$oG5oeZ46sRbTPe@4S!*nqP(DULV3b*YmOeJc3}VHO-*BQwpPU~@MI9hLR{gcVl`qG$h(6kZuYDA za^6+`+d=-@2hX?|RQr2D zI-r!IVz$H>EM}!*;jGeczPa<|(~G{m^Nnfjty_E{pLORBo6V-s7)tjL1CGF!WRTiu zHGRAt!%hyhL95@!KoTe%^Mt4lepvji`v?`&bX%68?L|li)vEBAzu3F6A|d zi+f954ryU=TNxp&V#ItwXMNd;q^w(+Nu zJOhhHZIJ)XPt3?OCz&Jq#!dckuDr+EOE+xd0KIbE;MDhxb%>H#MVk=1QQtuSou!^y zV}fw%2uUr)a%EaYYYc;}yiQ=%r+8^E4RQDVU$ujLkMyHI%&m9%`~TrMhd`wF!UILM@?v={ zwc#|dQ@IFDdrKvm&fdg{g_JF9C>mG@MFlW2VEc#Ei2}(YGZ=_!sst5K^FwqA1%kC2 z!quNqM@eS!ZSAwXydzP_UA505-2Ga*td{nLY*}QbzscIjgIK$aM*c2Q@qOZ7*o_iJ zw{*efM<#YN+zocSc@GLA+Wt@K5wU z)NM!7@7lgSd*}7<)w6H#du-s<xbu7&wAnarHWU({QPz( zM2BtN-meJM^PsFvc${R#P>NMW-U&D_$;BtgkYs^(tT6|XmIn1L@saT{z?WJ)Oxj!^ z8NqQb9H2Qw{u3Pqm0i;{-LQ!z={wIKv}{dA(={t*>^1T$`fbeo^7kzMx##(Z-yY`w zStCEut3Mmit4BZ9m)&muMEB=^oJsxIw(Br+SxLnslCScY-&^wIzX!H$H*iqfwgVwc zE4fj94{Om)%F6Trc^{GmA^Bb4QB$bwF3G#Az|d50kFJr4H;QO&9SnkOGsTf?oi5uSrFXyP=t#i=? z{KKTB)|-Zfkevi+pN1>*rQI{Z>|q2pgGIpU5YBAH!100d@CP<@S&-OY>D<*eSV59d z1VJMn6RlsN5I|AT&0!y|aam?4T7=;s&65ZyhS&T8`VMzyR6~%&gPl1Tdvm&`)QG)V zEAZ{>zu zBRvy5CO?%mG|4yC?X)t!y6ksD<>g;b^4r6vpW6Gz;}`f{SL2@m%gq?|+SRok4?IC* zC`&H^C;JvKV_s5(6rE`?AUja-sL5ivf$RWA7YrOQfOU(GuJ5BGt`-j&TqNPBttz7g zX=FH9N%2;sB|=AkljFh5@YX55>vMkT3%>L8PwYj?qh{-V;#W5EpLg%#cQ$Qe-n(`) zuZ>D2|CDF*-z?Uva-0R*Djd0}ZSo1{JeD7YaOLja&3D4VEb(2tcCkKMo2OaNpiYrD z5aD4G_)FbLdx3gG<{>U`b1VWV;|IWW*Y#(d&$tSGUKK?G4n%_9I{8&H6`W;)2-~Em`WuQZierCfiT;S_| z<`;N=qV;1XWW^sVR{pti)gLSI0&JCqV& zNcRE~$j#LmgYVD$4cNh=a5h$LV)R-yAdw(MR-D-uJ^+P#o_hiN6}>*T-c>eRe==o1 zSI+qb*88KBk64ehoPWLLkKzNPF&@;G`?`Jl>u5{&&if*`eqHsa5aXC|i>Fr@iE%oRWa;p2AkQ6*1nOM_ONJM)Ek|B*xBgr9aq9H^ z3<@fK&k~<|j(_kyA5eZ{%^yWOM>6>gKXNJg9e(%>$>BBZj7`Me;wN>rX&h)HxdAAB zjvO8%&_?VmD5r=-GPJSwr#4AVCVC-LB;+$(EHx@2Yft0COJ)bZT?61{^exux<(K)n zTYMG2M1Nnta^*5d#dbb&6aQuVcK*vo-cdewWZn9=(8jls>yV5#`briFyah6j5i>Fr z2}MN>gDF#}9Z`s5kh+GWUQyB&FthtaN7>u*P*xe5Br?iGeTbF}h(L4T;BE7@gtC7f zpR>Iv$jqpd~P;zxbv_bh>be5WdpY!BA;D9E3S z)cUXy5gF{$+NgR#Q$t}yQa9}F3(7>@blRu2kF-WZkwmT$O4|nk8iaB4WEO`1hwEI> z#OIKvn)^A=0kCB)TcI0UhP24?`#Qe|uOht{|1fD8bf)(N7WL8fkg&Tr=p7cXC


    zdPkU-XR3!R$h2{hs zm1EzQvnJ6j_UwMp9!8aNEf2PB;IuDB0&M*9KbIm z&_j_-hqG=@PEM{)u7Fs=_&`-6n=nI?p?sJDMBT#L@XxG+fD(+lgH52>5A0>#KnV1p z^3AGbBL`$6YO7>-g&q&A)W8!y;|7bLI7 zd#~~?ax268!ao>`6FE!jjT3L2v`e&Tqc8IYsi`+BCE8v|2>`7_X-IIO;veK1#=}P7UUlgRP4!I&CK1!)xB6UIXj#3 zkSpq3h&mz8J;#n7RXk$YkfMS?`2+I$_Q~nhqg&U`9kbiF!wPEAtZ9?9#>q(ujT%yb z+7ce>h3F*x>k($K#i+H=q>b27U*j+EKMIGBM*3LuumASS2dy`4Sh(<|4K2EKY_9fr zX}$e*#rqpJ%v-eK#g<(=$;oMA`+ff`y-3UL%X*JrwQ@pQZokIk zo})K%)$`+)FCUlMJEwd-y-4fTE6w%>=JsprMfQi`7aVmaD9C1<)yU+MVx(L#x1CXB z2)aXbRCv8WUmuee!e;K05=o_o74Y7Q#6bl|B=&I)$wn?}*4^FR$;sX94r|iM&B-lD zMcFLj3W-Y9`{<)m!Fgm(@pEP|Kq{h=ONtpufKh%-NoNMYNBs;B_5!W=l`)xzdb4-S z3Jc!y*fq&x$NVDsF8_JH{}uk#0>(}ae9-+;9y2d02T#&`Cb!$Zu;8Qz{@Y$w!ESl; zL}NQ|_C623zniUJ$TRgP7kXbU;IFZ>`F!ZyvQ_!ad!aY&$Cd9v2iS}A)r`Kk%xs39 znN%G761t^>$fAxGc|uSK3`zF|&{e|q-9+KZdfvf)KKPayS`+?pHUu|RfWg7PKv;!` zNs2Fsxp06%51{&<(UxT9gI%Qy+VD2ZWlu1+gP*)*Wq9xHc}E6cy`aBy1;yb`T!C_W zod0x$-`=3VWBu6LX2iC^&zz^VsL!Nw<|tmukosokki%K0S4zonVDRj zToj{f60jDi)lgt`I>NE2D0z!DSBQSpH*`x(h_s|NPRK~is51H@Kq2knW(cvF;AvjO z{3uHV%n7%tui`X>-B)Ah4R<(UHnNPs8{fVB$#~W3CjZ9xpZC6e-=ClJKEyiyhqH
    6Ek7Nc!BJ9@)I!>&`RIgR{SH);F6=I-FF7-{+e@T*c&9 zdM)n5W}F)J-KZ0j*jM)9wf`pBlcle?r~Y%S6Fv5dTrD<;s5Jc96duCzz_AAd1AKLZ zF^Z8iiUIf&jzncht;@(HH>)bvoe6Skz#UNt!vIn=tQAuE@T32{q6xnw_vP227617F zPL}(B2tGud!`xZ-624q#J0kvBJG9IGK|6FWS$pyuawmRWm57%SDKmF%?Lh8}tquSp z4w)HT=6~1^(THxlT0~C;y*LB z=n)D1S1609HkV|D)*S3tPbwpI&zkZPVhM!xGD@p@RgdZ(DF_H4QY6D9m8D@=e5?=> zw2X??M`H1bK&<0+5H!S}C=MyU0X$()aF7{++wjmJOR&Yy$1Bhr2r+^5m|U^6BBBMO zE1m4ZJk&yx;Yfs4w7{(DBQIQf=h-E9o?U+DnP>jJeBR^SdDblU$CNp5q`)(Tz%nQaiqLbVS5Rg&fI>O~3OinEv)COjM7=Lb3iS8(stWT`iWQxu zm>Q;NnhOy|MLUv+Rm@_Za34N%x3F)&!G*bf3zg9Yeewzl0UI=C@*=i@@99ub)IMwQ z;CDL?F3iq??@%f8K!1L&vp;AmTFI(9giVutP7cjPe-I#)pa|EvlKO)+;Betz_s3Sa zOdtWG975RTbC^5GVW0dRW!efEL$8V|o@awmvkev72CJLVp`tGBi;CK}EiAm0T{yTy z`#}ZRb6ELhtIoQ;aLEt-!FDgo`7t4|p z2F1P}8QG&rgGT-yt}d=#I6xy%5y{uB#JO|xM&U+1s@*v0O)%}^+)W;)kPuXPWaX39 z@s(yHqR+s?;b=#FKtC`=`teOA*30~bl0S_lC;6N2okTE)l`mH}S5Cv+RpE*~;co#b z9H0H~aPcfPJUtxum!peVA%8>NeEmEx!oVTsxnJGP*FOAA8d`wG5#&Son8^ija54>x zkLDo$R_lX05bBU@RLm{^oxi~b7Re20de3XvoANg3r>}UXjhBjoL-VmW*pUAS>dFYM zqJ82_p;SttcczB3@R;&8@SJDXk@|2CeYog&Khav;5S`HUS(1;3?uQoo zgd584F?ad2^_PRx!jHxMoxazX0PSwrXr-b%%_T5ka?yjP*X`*sO{f`&uuW8~Kcm zKC{j{Yl>IItH+#;IsBm9uJ2$B1_t`UWOc0+VGO{t>~l(1A@IjIH}ON>LRzLE?iVJV z9p+(1Kn2@gYKrEw4v(mq*W~=vOLu=~d7~fet4lPr?UVEIjxG}?0@w4Pt-7}IC-ICl zVc9gK0Gaj-GNb7kQW5qvMe4;QR~JAtpjS%(DmcJfV&HMqAb=^}Do>trp?Sq(U0mL% zQGLtqs%vxld`!>U@iFzOyrEj4+XMc0qht}!^S9Yf2CYb^mg#4dZ72vc>~s@DHd|-i z5Pq|wprVj}3D(>C{9tH43*%qrTWHIo`<~SIv0ec)fLXF-CU`=mSQifw3&HH*uRQ#+ z?`x<8?;@F*nW$F8IzUxOUjeh%a@q)RU$J)9HluOHz61I5m96{SvV&YQrS!fXb4}ZLm{EiE;ISKSXjt z5hUjC2b)cSx6u`?0@e%fqhJe`8kLy=_CPm^PG2bavI3+UvX^Kxdc@~QM2iOHt6kFc zDGK>Z1Q^H94L%f-Z;WC^i+AUb>6+TNcS3Bo$5unrF}Yo4PifI&eCwz4_}}v}%mdG~uFfknEf{yYtQa9o9W0)@u0l-6he;v9{(fE_uFfQxQUn7I&n_d7 zjTo&uBE^Jo$`Ln~kKM=v38QsXucX!mTc(u^8xt9k*(;;|h^4cK51SC=m#7{d(6FPj zVD^-ui$;$x?w!&|w{YHwVKYi{v(v=|#Cc8UG=v%KCv+&Iy(!5QwXS$Ncf-(; zdq*AosbKwy{w@0+nK5NR-wB1idZa~K1{=0#E}2mLQva{dt^aIUuK&8=LC3c&STLgR zklcVteh?7MS=kQYBhMNp!}p#~?LFjzZHQdjArP`4J#Fm}GblaSqA)S|4L}+Qh@jsE zicf_`RQtocOegrJ0Di14L6Sl(j={cSiHN}ub~SpVqSxWhO((MRd!AmU9Y!k zI?*sWt>vVJ3p#arVM2Wcj%;N4iF6V^*u{h_a3-t`!??FwySxOWoYyZJZJ_Gp{XOhozJmGZF^>QQVa6C zwk{-WXu@Q|hUWCT`f|s^F-gPI^Vnd(Lz^~gxEc7%ei+Zm7|%$IXBd1eO{L9ZJi&Yq zfFCoM?5J3WktCV~j3gGG$sn7Gom^xCEWFt2VBwSro^?+KBKDN10$h_gz1G%<$!Lse=&TY%<*|UFM&mR2l zER18?NFQemV6V1As|PSQ=f*BVZq6L0AvcH3I)!0uo)8O}l*`6q-R!^^)`L{e79;2! zQ%^?7o-w0+2xB$h6@(F{1MweWog0vkRZJuS#yrVrWQB018yUo~f!)vb_ZQ<-u|yKG zIMNfO?>6@@U|!)ial?^k7dLI|@QkS%5UcN7SNR!!*G&z5tox*A7!&gK2HUfA{;TH% z#(0pYVIKRzsy2+%kj9!c0cm`9EP;&c(HW&r6Ijw%=@|9=h?^_61VJH835;=Yh@f(L z3G&L|nVJRri0N@q(K5pb(9uoUI0-s7lX_w2*BC)-e@CZhvmW`S%y{h?YH9UD{^|=j z51bGM^J?hnTU8TDP)ezmm}EK(KzU-r_C5?vwYsUX*)ygZ@{^MTmM^Req`FQW5@<|l z-M&w8dXTq1Kk(Y3nuygQD$lWkLyDaz$X~tn2fJ?HW01XVI0_eY-c@U+y;` zyFb+P0sVUyIn`Sgx{sG_Y%%5Ni2U^h8_&j1UB-L)hnMv0{@9$yTRAzlH3aw}8`{wi zZ3W>i+FCC-D3J6=Fjr72P~B~A7}cn{&AwFiJ%l~fZjl&?)G`QYjmAIrO6rQPgQ`2= zl@_tC$$}Qlt7bRC`sR1*+2`xI)jdhc3D51+IhJ>J><-4hpS}}sV44LlO^kGiq*hD# z^z#ARRA`8-r#cRB$pFv_r9}_iFnyw{;}ct^sD;!%6&%^9aZQ0Ty~rD|XAwcUK^76M zYRW^^K~Den;=Ig&;2b6U5x7B)4F6fNJszNP$$~~ zReZ3^-X{5RV^d%6GIPnXV@nowao+vK>=vDU6UX-J+e_c6Rom%}8*OZo^Vt@UT{jAQ zT7$Q(VJ8|!vu52(hkFbw#rkdwIc;RxjrgsoOOx5bA1;o1P%;sbmu)yQ(r6Ibf)Yln zoS+1Rk0uNm`VR0klOQ7jRWR^S7Fwl#5e!FcOt!T~TQ;43sTKqmpwgV&@(E)fZ+v;L z(_yy`z|!{`ncdrSD;pX7raJQ2%H1oEJv+Hc;-IlJ^GEu7j9tYKMfXOVrvby-!Rd9x zR1wUg7y!<#Xwmq+>-;|~ z^Itc6opE~IIy>`c{vOX@?Q(zCbpYmg5HQE#z+x*>nArWvp<%y2=ALzjwr97^0sO8Q zbmolH!SYks2jH{8ckh|D!-BWfX(!{Mj@))!T+_%$*d@i&de3_8w83kG(%3qR_7zt4 z4f?(e_j9pl>)C(_)P1xGzkrFsx)se5-~&+k2y4lVrg$8O&}cW&*4(?1(J z$%85`vwPgc%DG-?Ol^J!&j>M1fuA#8^w)w2E#kCvZ`@bo7!`Uxc21=|^OkR|%fPuXTIQRE}bK_ajcvcenW3p$52)kUX2qZdG3ylmX1H#buX*4lH+A}<< z{0BDs{CPg>jA=WN;+_?kWmoHO)E(>x{qYQcJR<+1KE!ZFR6Ikmx{ZA_$leMN~spv2WFB zHgwev#-_ASb#}ktrMAAgIXyEgd9z^+|4eB-w`6*oz!5E526Xpy_8w?%*`(o%Et8W{ z#5|Xug53NFoV$yL5P8{@?q+5Ynf>p z?3!j`v=Z?^SYbMZpQuE}EP5!(0ldA6NGw+h!AGK?6k%+HSN*CyS`3Bl&_WGD+V;uv zB05jn($a;e!53jXlF_E^B*~C*vuuv z;!?XsHSh`_K61>IP5G_j#TZre#Teaix(Hv3t@amjWSY=23=zfz$%9x20-TQ_LLmrX zx~ql=BANzoIypm0VX_9E*)%yYG2Gof)T>cjb&`@b?`ebnc}1TArXLy;==`8eD;Q4LAc^)JC%DEY0l1&W{$A552D%DxYEcBj`m1#no5)wnIeZls_F^6gOE1z4Xy=y)$|ACQ!Pu4kybubdgco{Cc6`frVujMS2Sh1O zEIn`xus-Rxg0v@aRzk_3umqw%$i@51%WzHw1~3FA0-^(>vA=`v0Ou6obq?p0U6-Q6 zNwZVQ>O;zu#_)%dPDq&=vugLw=d*@dT=q70S3LYOLMKOcC`|9<=ifQCZ+GNzt0S*$ zbUpBGljQ!^ANuI#d#Dq}uZ^$I8Z=B=Jl3^nqFBR4JlK{4WMh92IY6Y01_p>6AoxEh z2gu=_8aY6a2Am1DU<|T)D07yQd*ng{kSKLFD6>Wm5I#ms4>K!KXh-AZ##JbRhPv^n z*+Dr#5Iy&(2xNL_{vO(mNUCtkk?m_pwSBhe*-B;tU{_$qxXe*=1vwm zqz>FV5y{UqN<{CM$Zk$r$cbx_D-n z@nNC!^HyyN4CZH*PbWOvuHud0sGLrNyDwXs(V;VeU(u&w$V>CXp5~1VOnNUVMOtw;sf9R-(6B(BrV`-KYo$idR1lI?NbX(LgsCsp#UVYQ>;MRkO#)tT~D&p(dD@nmV znM3K%79$wT=R*=;pO@)J@fgUJC$v~wwmjs4e z%y;BwB!GVMs>klD-uzFtYWfI|5l_w<=`mCsm*o@nQ9Kns1d6V{pzY~m|3V&xgd9e_ z4(NIWwXuKU9mU}tTMlC=B`RVY60QY3ny+WY^cEs8bo_%cC<1n}tn`K2QtLA826PeV z)&DMkv1;eS%ppb24{;A#M5NVJR6e`yl@&Gf7fqA3{6+q_t`+4k@}H~o7oXQhh&5D| zg^QrQ7bKoUsYH7NI-;3OSK+hWkhvVofBFza5eS z$?oFAl#W=oXcNu|@^M0)RtkmxvnNsrE#7wjIWr@72xlDzMx^7ChciYg`*nB1=_Xmj zV$ZMh>AE5^Yzrj9+-)8u=61>LD%Z0b`wdljh1QN&p^u(NJ3Q<*feW4kC87(AQtW~f znFlgORY2G7?dk2M+UX9!YiagN2h7Z(#2E0u@-dGs-MR!xs;923Tk7Ne?7H_%qj>=P zo_l(DvwwrfZ5%Wa^oe5<)jJlg?pVG;(yqcn9J9}zs;z&URz*iyb@sWRkLv44=%|4t5E;ODRlKp~;>9i7 z;Oy+*qeuV#y%_8DLd(?T7A=xfTk@?LjZ*>XLCSnaRie84j_Vt+M^U1>$FP}7^h3Q= z%1huI(4&u@keXPjmFQt(Eq5KAMDi61s1naRMEBt=cF;v3!vUG>Rc=ddUa@xUONR+_ z(LltAH2@#hTo{d$m!4uX9*a4%jwiC_DXA?OLwB09ff zj*ab%SPmRnd9G8|Bqe4hI8EA=nACz-x=+TRt&&ajXC`zPU$tLlfKyMzY;3%$3QJ|4 z#H;#!xmBlFSLWx-W7K|Z`Dbhiue|N!`xVW#{Gf}~L;4h4Mk-EOTSVCzZaWz^lJE_I zu8PSWvFF>jScAdfZgBVZuSQlh!jpXTwr!R@=pqlXzNlPb-T2u5d|N-}r~mL#R*%Cg zS03X0>2kT0Pwm%_EwGkittUduxQTkPW+^w*$r(qu3)#%j7pa0vAV7r(7C>bu_^JYH z8FgIHWKr$jAG#tAZXGb>RTqhm;K=s$g!A81MAndr3}uk&V!&vHT28r4a)18%cR%_t zuP+jc8oD&@dagce$lujp`s$-ET%D{gJGF6DRD=-5l zJj9fNh{7j@Xcro%9mt}JEz*SSpW_tOi9=f)*YqhG**K+PY+Mu=E0CleE!roz-6?SC zr;|MGC4I4_d8MVpC19o`v8qJO7-Ws1Xb37+k9>7-^YcR=pE_zg{3uad_`6rH^GjQ% zzB8T8UcRRL%QL5M?ACwtn3*qkS1%vF8DZfM{F8gMPMXwWET6%D2>ys4JozT;^nUPq zW;hsPZ2DHk+Q9k$nltC-g22)5J?}g3rjQY-9AUvAf%|l(yf;|zsLlmnFp~?ABnxE( z4AcV53F1@$nQU-QBlJUJx3vFTjx5TJ*2(mLqx8Uu|Ec{Sa)4u7t&jVgrG(5lCzAn2 z83k5R2nkC}C6a3!(>P(RN#7U$h*Hi3v?iw#ksrcEkb)ls*^lvS92D$7GG7+QSwlm{ z?FW0*Ui5SOJpRFqNGmYE?-*`kmk!AE^#TQqtwarajJgT&G31;Sp#&eWzXR@!Ixk*Q zlv#(VA@vSco{}RO~RzB>9Ozp)jq*jq;{&dOU)+#LvX`Cxu$_^*}H$$jtt8w(q^r z?}e5$&umJElP4VZ!b?LQzEJ`#Ivgd>ZK-DCST}Dop%Tp@-XZv!7m|_4o(E(UK>;o9 zLg$p938sL}Aoli^^Pum1!^T!@O68TCpZRv-f^QcO&$sSpOLC`7$?ZR7icu>6NoRf# zx9sTrZ=POsYhkpm{{v~+6f?CxL}E~;|FTB^i3A;1+ZI{zJ-=7y7f$lEMCC;a-Vo2xBs;1 zs#Fo*RTugo<;=weUsF?kbo1_?9W!d|xN&1gjUhkQ{L0VO<;GPwTVi2P{yWnxzM*VT zW4+v+VcyavLNzEYqTDaS?+oRBX>4I_QK~r%_#y=$AIdeiVLxIXaVfGAwd-xE>9)M# zhK`rZvPkeJ3y$vo&{r~RTwR?`sM$dDWonS^0aUXts6hiJHArZX&?qj-QZL98Iivt) zz)|8vN$}BBgGMB~`lUecHis(~RcDTghZ<_DxRTsbPG_lRxbzgKll=3|J6Y_TmzdmM z=~=n=Or?~k?+Bg>=H6_`bHx*1EgZOY+_2T7+IsN7wX8Ki6(+xnLQ}WdkhecSz-Cw_ ztHho-@VyeD>vVYQ=taN2xbBZ7V|O0wq@1nCjs)`#kQ>SmcoB2a5cvV?Go2F~0mF~F zN`l~uV3O&Q(PW(G1TEYI#{*J)^(FWg9X+Mcfh0up5yw$=g_t6dF0asGB$7d!JoHUN z=|B1^RQb5LxTLrw(7$*!v>=<=*@$9{K0;Hc4Mwm9047RGJ)*BMX_4V!I$NbWIG~z= zv_XvDTW6LZf8*tsw)?Y2u6y}6uN~(%cFjMuZ3+abKe9xfSRY^hp?U9O5FW90YtHaL z#26|OPt6%Qc>4H~RaZ;KzCC~BrG$i&h1c%e4Vt;TSNmyFjm1t3_C(^;TthYWbg0_6ucPXrr8v3#IDZcagFhlv(3C zTU9@i9xu|Cac6NIgwWl`$0yJy&>JOd-~pmMNIC|5QfT8r)WKG-$t;G6{f-(8q2|*GQ z#TyhDNLa$w1LvDgnFp_%1aB2(wxUyV5&DxNck}S@@bU2RK|KQ%dFxmJbatmv!6CBJ zv?jfa^m;a;U3gp1s4>}_E~ks;fJO9idW`l%3fw zG8_&NTlOJBtPEZ?&4Y<0UImk<4J0g#_?N<>!T|dS@bd`u48>Xnu+dIfVOy(Sq;tfG zM5TB%(^ff36S5xH`I`q0ur}9ku+|3-@VBlnIRdJXB}?|(E&G(7Y?az^j6;Rq+K zz55eicfNepy_GBPKlj}Il`HS9(%-6ZsKd2lFbgQ*m0 zt1KWOAS58fC&t&}K_o6ntELl}(ARPdm_kImEvV0;Er_>S%qt@&9~i1DXv69^?9ga2 z|M+KQ@tO+LYC8m(3aLUxK^-)d1_6~xY;0_NY`kw|q_4#s?croZ z@$+z`_Sz0@NGWxI>b9W+2+Tph5Xh7;$?TzZf<4R92Mmi@xA2MOtjmfg7OjmLn$Obz z#rOyOkFB?wH~V%g3ax&~lufoiZRF)YOfBQLZ@$lNbN}~mGXJuvKQR49z6~e+EMD;P z3opFPHnQ1llN?YbQ%u^U`pag3y^K-e{aW z{Y>+sQHRHpe#oS2)_!t2xNhC-76kI5h!_FOgoX*0JeCDcG`vgbT2Q6xK+EN(v)>(S zg1kdmA}LrR?EJ_@U$pnMn9OO`ep44hJHI|ex5@z21wHHkSDRNDigdG#-vQTWi`ODm z0}X|$3IDB@sbS}qvk+EKWQdEgs4mH!4lBXwa%KKPPA4z?__X>*E7*dk+}UyHCEUOZ_3wB_?JiMHQ`2PHM-4 zz%+wG^Ml$Vdn60YrRb9^y{G!?nn9LK*ePucudalk?npiy((v8g@R>=m&p9XpDf9pr zg%8S`gv%Lh{m%!qo@^nEc?AP4M%Z z+)Rk;;bNn{szYLlq^D{tSne^eWZYQByf(l{J7tu_f*JkVSf1d|js^41qCdajnZ)Zr z&xBp&@H{*SRO<9pKsviU{6q=}V?kCuE%$Gp1|47ZSC*LqA#KGZjqWR8xuzmPf6LYiPjeR}rrPJ?nkiHGSGQZTV-&hjdD!$io%7p6baVq;T6p0yu@7Y)=)0g9<^5SUKYb| zXyg8g>Y;!XO{}vZaWZ(gBEP_PGz5FfV6>r`H-nK6{tCbW^kNZzaxbOD_=$6spxOKs zi!nSsd2IhBO^ceJx#-Uhp+tmq1NZ&ic3+=Nuj+e3#F?@FP3Qa6JYGuxcjxgx;yp3ac$bLHV?}>5DU7FK-cIV zxirnRDv3Ac(W{bG%~)Nll5e{fl{htLVC{7|V%V_Olajlq?nGN1*JP{A=Kp;=Rq*lE zxf&hXSjTGo`!?#Nw@JsuTJ)D9;bUw}mFy%V%sai&G(rnDAjVw$kfNECl+-w>u~>$r zY?49tXl3&ed6%>xAEkoI<&R#OkJ3u@%j;aGHXXwF4)S&Vu;a87tF$S!7bq`)84~6a znaAWf1K<#>GSHlQ`ukIzBmW?O01q9j9g!g*L`FoA;48&;NUU!5E}BvONqH|PU$ovD z(I;Oh2Ah6nE}PX=nkLY8kUXfhrVha2u5Bc>+n6fa*Z{*0`w?w~vJ8c01Wf}gB)yBF zh15Elfe2FdMWgJks1@&^fmT8uHK2ynI)@FTuwAV$k1$j~$+OkI!cmJyBeu$_OLV~w z@luTTEZfv@vk&5frCqIULK`(_-8kD#XwtQe4u#XCr1RrU4 zLX|VR%fGd|#M$q{SVQ%sqQchr$L+CgSq(MS|74Xq5A(D7ULllr_2_;*`X16X)kDq@ zd$hlpFKp73l>-&dli5auHfazPVB5d;CrfEIe*C=ZtsWOL=`-9TQ{2NV)7}1kjEA^G z^|wEJ_}dJ1z0c~l>fgtBG}WN*o)p*>RB)&}X{*=`@CV+-{!Y9caPrkkAvKdD!1J*3 z3x8er9+U6=E{q2zy~Z2pP4Z5}my$B$J&^4VHey>^9@vnD4A>#6Vj3yy6V$-|3NnM4 zSs&AgrR(AMiSl!0hA6XKV&?0|uxWh$7&g>=!_0<`;q%$FF?_w5H(*=8ee=|*H@{_D zd4KH{?~l95Qa98_-;LY`H+Y`&GhM0rj>c`ti(@f%nYFQU{c6m&r(}^PWJW$T zvJv6P0;(5`(v#R)%x(x6zw{81Em}xpcRx+h$pe(3>~%Lm0$s=aS=SPWHT#pfq*NZ?v!~G3$Ky>@2Df{PE}mz zT#4y7uy6l@g8qF6T7#)p(2%~lgAG^jl^RPQY-(Rv*d9N+rM4GXw+H0YhEckkI8n$N zfx`pwI5fZx))a=Qz@6A^0Sj;lggap(oG$EO4NevU&d@aV$I{NXSCo8s?#;ntU+OX` z@4MyWFQ0mS@SvA<9WJzds`uF5*-bxaHJN_Xo-qjP;zVTu)8zT7 zQ#z}@rhx07wo7y(Kfqt(9Y~@>+47AfxQm|5e|-GjVR+6>X`=q3zCZeyk(mx@gs>t3 z0YNKZl>q}nXDL;*F9eacPb&5ae>eyeK@{!{T#2_Rd4n`<1HgY~RINhP3AIXq6G#3= zxc(yF{O2~1sQt3-PrjM;`(@jI*iU!2{lfam;~#AQl^!GV2+U$Cd2R-mEqy%cf$($tE)=>A9-&A zR%P+NkKgmI=NwQ0QBhGrP%*Vo0m&^~Lq#QX7k6B9K{In9#WFP`B_*@M9SakU6!#_f z+)_#{ODnfh(+tZ@SziA495_mTwB@^9*Z=yV_nCQTo_XeZXXc%Ko?)HQE~C`nP;oty zRMq~5O7h}z#Y058w-t$kaW4{=2e>{^-r3ctQpqQve88W&8nR+4*0m^ZSBtxME4o8Q zSy5zZUu252u1jU|zjxWRCN7&yPYv4(wi%Ojstsc<%iCaX7pAB`OgAPa73=ZHl|!@f zWUVVv`|i1Y$+zO zD`#re4R+;CgV5kcbsIe@hf4iL4*3`V6dq-7Q-3a$&_g^6 zL&~b>A)Yd%yz1s&bZjM@2P3(`ko9yJm3$UmiT4_9e(RV4Yx9!PQJCs#td%)HNC zw&Iz<5fxxWaIN1}hc`msh#0Pvd)P{;&h6H0o=ft4h8s^Bln`!S?w+bcb92aI$#H_F z4beKdzC_?rUn04?Iroy3=7F!mQG*;gPyt!7BJ`+NFYfNAOcRxrY57yF3)a-A*x0D{ z0|%-niY&SFA9PU?*waYJPbffs0_`XIMn?AO6B*gpW#*M|*<2>erHTm!+Z4m>T9hXD z`U&^D2W*15b*r#Qvhz@^NLDV^iFlCeetG;Ws{4iUuc(|QG2mg5-7AI^%vB8kaX+DW zF)MBtu1c_fQ9*mM$kcDTidEj_d$x-%8~X@_b(5HDZFX?7i)mx z$^Iz2c(8_b)wC}2ezxS_aAijR&-ZM#kQt9`>b3@}w9In)t_M`o1KD}J9n z@A|CY@{|ju-yPj^O1-6F--r!Xow%__l}fK?zg;V%;589)?X|cm(c$lPn3CVeW1u|Z zKoO>JPt*6de(a?-!Qcq*$7^`Wy_c$1rxH6Dz8r%|%^h-DfEx;Vc$i`0+RJXo5P1cP zlvvJxP|+eixY{N+L_Xl>yapvaj1fFhtxipYyR>+Qw}Dz+?$MF!gQmAbn5A7u4DVe4 zQ2hz+k23jRsDfHm!m1bUOer@-oE5R--wSB}+qL(z28i~lFE4Kzd~tuD*-v+vT`#J2 z?TQ^amr*OGuC@G>+Pq`0YGP35?t`NI)mN;R7sZ~TqV)2EqSVwA2`_R5d--oxjSXkJ zE!&03x{Fa4es4B2e;=J%$B4tubom2!#RVbP(zEWgDG_@ z)c#9)wj}pe4Gw*!P62q*w8J&tr1u@2V)alz4QRsq&5oN-H6*n`w#Zyf*hg+?n zYLzw3A&b@HeETQ3(rhZiOmG*R40G#YrzRMK?W(B`WC3#)IL^&D9M0WOvJId@7@^n{ zVN2k;mJDQbW6k(iL6{0%o{enXJfdNEfM*%6y1rFQ*DN7ZfrniK!D;5gdlCGLH1}xS z0L#57JY*hf@R$eeJ#3mi_VHNb6)elqT-EZIZ3Ont1IU1Pkr%~d&;+~Z+)P)n$o;ohtBP~Elg|v^u$tH$6>vm2&&}QrEXNk(!+YmJc;EpZ?3r? zL0pe`3cDi8JYJ`Et;Z_)R&dUi39TZFvTItbmQ>)xLMlrzhq1aoxIGmT%g^5-HwCg3 zvu^0m*?=LODl42Nt$8nq&Mj-YIqcB^WV2GlzKB+GmGs=!flm zygcT_s1}o{Tp7Yl{J9}?@!_4%>-SolR{iApMvE~sevNC^J+e`gHq`0kZd_#!OepF*HHZk|m&=ww^pGY;GF+nN z&Ngsh@yNLgZB6zDXCn+eV z(?U7j42Q4KyD$#B~3M5*L^L&I*^%t_uME&1<+`i?0Lq?!^2i1>ajoi_%PqbV;MU9w-dXWOP7C$OKz*klHbH7&4VV>lR6OfvxN$c!*@woN*i^AXShMVwvky{XJzi55$ zW^5GE>yIp5d~Ad0WPP;Z_@YI})>#?slO8U(#7T%3>G1w;q^tmyDv4Y8xkH`FW6V86$c7!N&NAb zUOFDX#L695p~66+|JAdVKYaMGVZ(=u58CV<+j&e}m$7@Fj@&c0OWc^wWA{WlZFJis zbM_E_N6mdZ@{j$+g7bQR-V0O-jUvK1yU#q* z6QsT>op**7ZOR-|c!8MgeR5_)paPYmQ6-u?_hMEOm86CYd3jjTUd*zE8-1yGR&Mim zZUt6;`ri5^Jwr4*{gLom@n*yt+)JeFHGs~i!7FJBXb4^O-D-iQ?I z`#*B6uM^|n`e}xgQ9*pc1^o{ST$XF%+ctW+b1_1yv6JT)c`J{?H!bV{i!sja^M#ju z%A*i$VB}KGUl0|}@l+*|{{x(46IH}czPRM}cQj-sXOPSJaD#Sb-*SOIfu8(2nM!3v zS$Pcqek!rScXpRT14=42iQ4nOd;bFwxMl7~{VRHL2jSWB6$fRs-E;i2 zeKRwA#cZg&Qw+LuR}9$RcS^8w_YQukJEc#7sPpShQ7!T2JMZ3}TbQpb54M}k*FbdL z=-@&Ox7!xo_a*n>p}#zUz}8q^{a)A>bG}MCNZ-P}{A2EW_K2Vc1hNX`@wL*`0;!p- zk~|($xL>(!x%%>ZyuWQ(-kDhVEqNZ|#^vB|7QXv=J&*8T-v3fEF$+`oL+x|^G${*;oK`FdU*HUFiB``*iw#g`@b7`O5)V&2R94S4P{BA9I* zi>1543&9Abz^J)WkJ4$ndC{w+r@C#IUF^*RtZADk&P;moy z-G+LgrU;-#o|CDsTh?>dWvls*5#OuUgMq-wd{nSS^1xvU7aJXfbfOXduQ|8CKeGE>KS9d#qzm{zJZp z8~3o)MB9j`p>ynFu(v}!@*F7{ocbtSt&v|Yx2*M*XBPNcetvSE(66RnP=J30pK@h9 zO1f7mQH7gb-GrN)^YuLa@4Z$)8GqM%m0_017vrlIX{Auz^WRrS^R?c-bdj$$c{Pt_ z*X5~%qt;i}ms|2gsCC$iTCrr+iUl@Yv)WrH23serw%0}SN)az&hl>GXe44e-`n9=r z%i60Mc}KSHY;`b$bLAWkI^@0NrI|yG(26a<;D~|tQi|eLLUFh$j$!VeikpYQ-O_G4 zRchVKK6xeweYt!s-?Me=#*J$RvBpO0j;%YidAf0{#x0*}5*AXwZtcf{8r5t>ZH$$8 z^>B?cH43w~^rN!2e(1d{4GpO3%w-W!rYOJtX)8<@+0%30)LZ|43K{eAU$B0&hF-eJ z9mf@w*PTV+lGQKo>nGl^j$5CI5K(h&lRVyd6Q-Py*Scmhc*N}-P zCf8WJYWdsOM5d@cnERfXWiZ%FK zqE*F%S)Y8@_m1^j*6s?{Ht&t1`DG5H#~;{dyh~%>4B04I*s!*E~2m{^EfH zBJ|^*tRB{d)6RP?)`_~5@t@qE$u^~!j8(mAaF_Bu8H?Z9$Svm1JJRHRBs@~WYZ~oq zX1(@y*rgf4zUYOYYrJojTds}#jmhx)G z=0~j$KYQaE>lA@sEf2E>?|BN4vn#yvkcxA76iXh8pw=fNfd@kPv0@;QzhE2ym+Ikh3?OT5(k|QdVaJ=;u7>#_kC>x|(II?cGjs#?*Rq-(fLM z;F@T$!Q1+z!v5pduebV~-9G1J3uR{RYfAHwhF3+JsQitnEI#-t!#eWm{!7+u>&Thi z*5M7q+W$n^Ggz-@aGl=H69^7F74>o2huc32*X!LV9cvHgiNlH!^2u3I4&nBLOm8oF zERYJcINKyoka5L%jA)@uxsdmk^>yAn?X~=F`mG$BvgYT!Sal_XYBDZkZBd<7pI>(W zh(6Ft2)m(CO@L97<7b28X9o`!IHcZ7?h>$Zmw>Fh3gzG=PhWR-MHqibh@T%2(kP@+ zSg2opzj}3Q)nt7pzvwchspZ)X4*ycz55Y#oRhRi!^S@t}71hxkf){Dq;UMl_wdUz8 zF4Z%Xal%u)ec>GIw)57Hq5`Yn&b$!o3+waURKVZS+Gu^d&OCACz;|Na_A|$~EQ{&V z#fGa^7wg-3*0)xdt4Fzoq~`(=D`rpPzLEnkSXtJ`+LqzlKJ51Xrst2`oOt&jTYzDr zO{Z?%r8)}}RO{|GkLlyhk#S>0^CF1Rn21*+Ugq{N4aGndXP4@q3tKZ;GomjQE6eKenP-|bsaaFO#0k%j89i#)kY@%x z6Wb@IXOGUEnnXA0*fz3ttB9sG8`o@9R6$nvufiTj8CH$nO6dxZs2cr+Oj5Q9HHEW| zWc%k|znZgZlr7-B#=~DGu2UmQv1DeDLdAsCKPo#bQawuj(hsRb^&RZOuz|U7Uc4>5 z)DL8)oIlS@xtPCwvr7oOR#zONwb zP&-y-_LeT~?^U`CPlvm?t_9=QqRR7G@=~EI2Bu0XMIntg9qOz zvvYpOxxF&9wv9S;aNM|I>)2tMJtK3*g54gk?_rXjn!UQo3tdA)W1?ns=sk7nqK}iU z&!px<+3xUSZ1+Ug-KBX3&hFu+aVqOx)$L($_FLB8+{@!Ulul)Dg-(T6F35B0g|~w9 zC@^>PczKyz_p|dx1peoxFr4cy{3)%hQOhKWc50cqqTL>AgPxb2WNlRQex7T6wA(zH z6RID{3DXbXsm(>L{W*=O(eFS`LkdOto>mqcWwhy>8Q`8!X6mE#SIEN%0dmU}r`Q9U z3ONN@)+lo;aKhf?MY~4_-VXazRNr?%R6n3B4o~RWGa>w??_PiXJ7*f??lr;+F4!XY z?Np1X${559xfk7edZ}>Amikgi$%z0jPtEYDrIz6)K>sjQ<`tDM*0;`wDgL&Ii|-}o z3T~uico<(R{*Gz{!Mqb{Ae-w^WsiAor@=G`a zO8I5jf))QLtc`G4)>PL|K6RJz9{HC-)S~i3FbLBQACwJ~`P47p^HUZL=I>IDhUWjHaJnoT^Ycr{ zfHGWvo-=Cr596DfQ{{j&KCI$+XB6i#8shPYT5|DAaPP3-OphG(t8%t~w8|+dxgb&5 zSI&i^p=cjIBGPTnFc8(V~Zir?dY7Bex4fAhUXL%xPzBV$a0_PNZGT8k% z+^G?VT>_Qo3)z*C>q5?*;YBg=^6Y**JE8gCi?kfC11XABPJ9%3Yi#XG@a&VjoobxD zJPcEwMK6*=r>2xY$ztu77rEV+r>wkCPM|%DMu0s#z_vDB1h4X<_UL+KDKDmO&MFKwX+EDqx z6=^vwT7h=H3KHbEH-i0~As~KG}s?DOQamlsy0bP23Vy(W=%F4Fh`MHOc zW^0_|TYtSMy|2}b^E3N#s|WkohmDV|!S+rZ0UPTbaxB5iWVtDuJF>Y=JFjcv?%heB z?B;LcUSq@SyZ6l9vwQBYy>s?4tS;yL%}u;JRNj|ZcwX$)!WGrW*wGYHPwbGA_7bXT zI(y-^8a(N*DE!(vo6*mW3Z7Zc`Oyclhwdcf%G|Jf0UtuzBcfXMY8A?R@bb6Xk5vn< z9_&}Sd|-t@4Glz|SLsMNh ze!5Iq-v95;_bW(eemuu|wgSr@wLww+swp)IaE!@YpEK6}UR)t7 z?P24swH%Urpc->TYS8lMT2w7zGU-^z%E>cC=Ws zL@3pNwyIy|2()Y|Wk-JV($-JbvCEf_&DZj`E0IZ`D?`P8mKOzEILZvBe0~U!I*88*y)_~%+T)f!Y*3k;IP8n5iSk-v@W-Ddee~Nth^4Zz>>*?D;9H8iw zy*Yc&KKgeo>mGP^g9lYL$*)?DDa%1#Q7H@z<%C_;_Jxbh&XQe@2lcd9?|I+zaV}>R z-WTfJ+G67s=l-hH#1)|O#q5Hk>KfXy1=FpYGm@=M$}{2#rEA{JxnjVZ{^Dh8iiG}M zPF3~1l_JhLbhk^iJHl*Ke_yt(4ncjj6<-r^+5DVBs((cdZ5vtr6S)q`9@iC3(b zsNQL$I-j4#NU>nyLZ@t_@)zn=Str)!EY;5uZvI?uX#9lYxTiy?sa)@)hT{Y#G;4d2 zz&mVA!OI(^a#B^1$4HF+9EsPIQ;TMQ7Uaj|wi#%ub^L93r8kIwTn zptGvt)H3gA<^#1;N@eC@)oY9r^~}Xnr)_*YAtz_*lo=BzEz_+p)CIh6m>tkMZ%>@D zI>pNV=tJwbMdIc@kFKwXYu_0|rwK7=lvvPb@Tl=ar(Ju#d!Oe=cAd6&S$8^>7&B(p2Ol(U(V~Had8A?#*I}!&F0YMnv_$F#nMtyQ%UxcL{v%zA zGRApX_Py(%ADS!`Bc6OPG!?x*@|NvlS zZU4i>eZ;Qhf04+c54HqVC1 zx-f>i3x~t6z5IgNN9H=Ns_VQyoezGMEAcw3vSh#tk#}IqpHeJt+@0tzI4kGLE~Krma#_v6b z59_6RDL3+8R;D=Nm3KpKyOyxoR-HJps@?PQ11)?PIc}8tSY~W2dYQt46LjxghIqJ_ zQXLMr{@icwR^vb3i97J=;iLgKqTdhp;LG=js2Wg}Z;qNZs@4gpQ>kKE-gf$@bntw1 zIy|BX{7s68%m?%RQR!mbbhZ30lb;s*0Q|nRZzr#roZh!oP$}V$HMhR-9bV+_^prpL-`oP(?C{TAkJkT7X#DT_ zTF1=w1$Brp!bY4WKK{yMyy1Feh0tcUY|!YOpLdm!HBW-Es!wylITAB{JqANPHp zx1VKLnh!_8M_evGFJUbI$2k5kXvw!=|3m0`Uod65iP4lZ#^#1mj>Z^iKZED&UtzRE z!zf!4M%tor@;}0krJOShJ~N~_7Vp`bFdZahnz-x_Go$JMOc9;5KMIfGyzQ^B8u3p5 zJ*3-?^Nwpc@7|^`jD0IUvERT}`zh>nH?YHPCAPUO!zXUXu+woHTba*W9X+wbQGsfW z7>9iU2K*JRq*4BmC|F6XCO#sL5+{n>muQ#q5}%h7`{))(d_wzDy$(1 z{-OUG3Vtab&e(}~#{H7*3#RAum~0zC`@49_+=t2L*O=_0HLH{EM_hD08(tXrAk2;| zOAmqVEmSd?Pv*OY{zfRv%zvO@={>jB2s1(pKC=DFygB!0I4b}7+17x0{!h@cAnPyb zdgC|g@7PU#&$C<>$EpXOi~Ife?J55emyEACTyd-|xMs^KxNh%>VQyXpHym#k9Cg$# zxao*0$hHqIxZ&ip5+nY^uPCfz3+o{p)8B()uwuS|_i?=h5J!tqSn4Qt#p_qjh zHZQa@Y5pzdproX+&qgDs*%@8kG<0-Z#CX3!m@B;fO+3qW%sS3z6Dzpy^q*k4(E`){ z9STzZ9sS?(-(Q@cqbjBqhj9Tj{v8UA{yX}=@_&W)8AKYN6=i=x z9_`bW9bCBH!FJrq!^ta^&soHq_aWU2UehNz%`B~T!5aGLm1_7Civai|EN(v)yLJJa2n{>v!idpKl!o#x(x z-L5?OE7RVQv`bkAizD-a=i+|PLhWkqLlHd479Cez~ejhW; z4wzxq#|+yZEYTV=-<&8&rrAhKL?bmBebgm5aNiHzX`iSyEZ9Ty-6Hp;G$+yR6)lE5 zoFI>Tc7}ao9_v)8E1HV4YGNX?%9^xk-M_(BnHBEOmSJWFB#xE&FUn4BoPp$8ED5_ShC-p=~GoI&pZ{mWL?YqJkr~uL};? z+oGwPz2Jx=t>BR3m4eS583kGPyR?@qIArhr_cTs^+ zPxiY9qqQR&9V95@HlA-kZ;m2n5LXIiHGiRHo%U&KIn5!|J zWo@N-1Fh}u9Dh8|?{yOnQKlLH7ci1>NWac<(u+D4%P?+8>VpQSFzqW128&Fqa7)eoPkeg)n|=4*u}me6e}@h+bOXkH@j5(oKQl4ct5Dlvi> zPK-8QMK_k=)n*Lk?!__bJ+MtKi8)O7H2oDcWL_NlpMdQJEaEEaT-!*tQT_4p|0Ys|%pI0mY3kHioOeGWz$i&@47@%_pYNrY?@7SQb{JkPqTquGV% zgN~fXIApI#zcKWSAg&eJ8_^t3-na3*`6H(5d4=_m>4}4MKWOwLZC%pPvT@ zF=v0YGJ=>_8?`xTYOH6zeT^|@EZVU??8bWaCHJpcw+vz(6NL7TqlhxYu+cfj>5Z+n z=g`nOhvS;pa&TIXw#!0W`aNHulb4fRYVP~*@ionNagjh?cw0~|k zb-LxzzMse@4iwoX-L|6VZ<)@v_dz4{o|sg-cR_8ZHUjeSbm??L)i#wtEb_d{w$ti9(~f$@1b zv4(AFrqL2>%x8ty1E|So34+(}bFo6VKp^~n9laCkD#C43;;xU=?ob&nZ zoi`d_o?{f-nQ}O1_QQNzW#Op~C|GHh$E)TWc-aWUNG%jm+9J%-p5nHbMd-vd@1Pt; zi1s$^(=b``6gLV^Y1L`JO5O|Dr(+!27}CFqc%l=v`}Y$PD}?Tvl^PjU8HA#yWISk>j&g~LL7x7=M-!?7I7W!d6l5& zoS^%!r13$CJUbk@G0^jOpk(eyxaa24?bl^Um8rbhj6g?wnUH>L;5O zaM+O`%9iAHwUubP>@@q)tsmX`x$KSvr6WAYv55nzTyzwXO!Ywz62xQ$uf`FUE z8~Duy#|6>Cwo6ng1QOjM=uhx{B3vlZ4>wBmqkCP>6C_}><0jLf5I!!`K2Z3$wI&T= z?7?pmr6S=+!~%KD$-Lo3&(iJx1;omC|9=6pA1B9NuCZK6&L^4K?BA8gP4?y9cdX(w z$AGjiXWwrL+s6$~|LL5sFT@*i3`zgPd_K;LGR|w=vBTsTj%{&gvkc~N9I@QJHQU$- zt|yhkjFN3=e~bO#FY$$)b1jYv>{s@oIT(L9%G1tqFxMsSN|1hzZ6L29^ucyHKIC}$ zhHD+cRvDE`uBA+_Irh>>v6aPq`*d72N}#%ZAl@rcmGnK)+%c4WPT4mlTw`4=01E4` zNEmtLHFu>2fG1(kyDP`&&T+Y1E0OOk*DW~D?;N|!^)5LF(7NCl?716R@6DC(r1z5h z=5lUJQ$8~u7c$saJZv7r%e?Ee4eUAJ%RYwgi6c&w1Z%^*5_HekJ@7fqTu<`JlJ%_K z1#jr?7{})yVCH-eBj-z+7kDSV!(Ik`ii|Z1M_W;ea#5bHfK2vfmqU{Ej**=qdJ8}ibAuq~tie3_f znK$E^mhUs22HVPDx9qPw#{+Uq5XL-Gu%7&8FyG?z!}wTpuCOr`&J6<8qO)t1Kj!~N*YylTMqIhcHJ$+=>hXUyLC zoEN>8*1TxGRglLq_!OCEa!gLS(m0k$)vn<)WhTdD?{hp5gpXvIRm*cep(kDyS1_Uo z18ELq7;Y!Z>)`PY=aJg+JKy(MOy=`6 zM@1w{(0x3yjrEmWhnO$tC=AXkP_{#QPxMkcaL&04X0lAK)Vtw0%f=_N3=zvD)NaUd zeh0lc2I?hR@mqR>VY*PJWa9H8Gnq1_>7}{1fOCy}AKCxoyXGFIo#*&+5(yqM$QemD zrl0)F8^C9)kcRaea^*LElIs~kbY}PlO*eE;!XMeQpRN9k2S;vSy#)sM>($|>u_0L$nwWHhmg-y(pDfmC=1^+ zSH9z$eEyhbbDDg|oY#pTDR*V|q3M6O1^sFfy9m}R&iAWMvmWVI5mN}#<%}xwdzz*M z_hIGAuuSW`J~SC$&PAH!pDV-nBz6;n=--Xz45AJp-P;g!&pk$LVYnQc&l3x1XP-TX z@9IqZXqsaQ=`Y=7zLcfRQJNjWoZp*byNz6?k*tH~@p+-GG^TK_YAn-phI4L{^Oq{; z7Y(`A!g)VAMM@qB6M9+vM0+;tjVYQt5;(6oO*@3Iw1fDX z{@)PiTz-pJ;e8L2p{ zkLBEuf$c_HBr5eV&WJ>c(GPygAWV?YN@un$o=9=dX?EqDTn|jL_mzFw9CwxnISyj^ z%(>{c7KfD`{O+otvT=~})m%5@S>0o-AJ?&tn!+-b?5s=6<5zn>WY9gseK=05&w@vx zWtqt}N>t+pp)O8w`wwMEgzsZdDbD#<%;vB|0s$$3uMqggLEH<6ExS6ZXNyClWzTK zAhRE_9oS$a5`c{%L^`mE@^7O1X7bsb2W(;3Z63h(3BV4TJEnu%6N!BU{Xa=0D90|+ z?IxeyblY_1M4bU)cV@BPWtbewlIuq>T)swJ=U5;cC^!m%b37t) z3qtiKE^*Y5MZLt=Aw(x_nvs$(DsHB5*Zl@vK>Cc%wQ7VIqs%k@c`GDPP2yccj z69Q4TJ4Cq{f?<3%LzGX0sE`a%k#^sc5S2++g}nVbL->0TjI(M2M8E`yzz`xEqPmSB zeGSG_BMTxZ9-?L-LAf5A1yL&+qP8EA0a0ft#N)mYPe{-|_zpx}hONhV>+gaH837U6 z5+V%5Qiujb_-TlSu@H?TAetP6Xv#1VjJw%5@Y-RBmQfI`_Cd5Jp56`-Ng3Ms5cF%C zNRYlAX`{Re%GTaSBoVhDI;4Y_w-5^~)CeV~9M6aR(t@V7T$I1m$@#f*{Wc zz65zqWSEJhpEQEF1TlFQL_B%KGu%u42%1yKYiey`Gmj+EK8>=zOxa!`uUE)pdK{13 zlHaSOok6)~q(jVX3^B__oP>BS31YT8LHE~#2*#htuy2spoD7I0#y1ZT^KU`CMf$ht z_6}*@BW()bad8yHQu14t4Uwuryw9*7kY-gH#Og~BYsNu*ME8#w-#YSKPqz)J5F5#F zGtI5!zwIc*j-?QvoP*ds0b(!TVV^g|{$Pj$bUzpiafq}>$m3`n#AoDxjIv}!Kzz=y zCr(40%7*wV58@jQ;;bLUx1|5RHpF>`x#$aV=?=u@aEPnqcP$8l-@5oE8R8~o|MeWi z?+kaFyzemHySI4el(OcNkF}YzI0@YG70(mc!MwC8o7ctj@+$6dRoJ6b+=8IEpM+A9 z{-sVs@u2A$3&pE8l+wHt(>oDLnItG>Mn&6G95|-UflE~!#5)D#*C{;5|pOo z`4pccT0&_?8JgumX?_Vx3&z(9#1$y5PeO@g9BoL~HWf;{g;1iVLur2$N=NdDrrR@& zqfKLE65Zq4e`081De`9e57P zAch@GzoGOSb_)u>7iDBPlu=nw;^;Q!B$Tm{P@WGa7}vP%P+o|EGM@6kNV*p(*96Km zk@8H6fHK(+%npKXFO7pTr6rW9fdu&`1Q8j;B`DL#=jGE-*oRQ2CqsEP8p;fYo6(&h z%}nx{$?!AJL7Bz)USs^PErl}MgCMWhL!cy*CXw+ZW<%jOq0FI7b5fwpWqfnVJ1Gmw zo8&QX0+eJBOpCV|{w;=IK)1J}2-+9&{T7~t!f!`;H-xwaJ>X4H zmQU$+ko1QpKsij>BYc;mjPEn@JQ8=k#3iQh+R;AU>rZTgmT$NFpet+p%b;Mix|a7gSq1R6FgC=}_IgiPKQslc1LH zBhEoBxfE)t2~a(vp|WmKy*fiJO+Rn)^*#!<4E@T|ty~0D9}nUd)C!SME7m6RxDcj6 zt?Ul93jO>7q53a`TD2vS4K*MYYG4w9&+#C50x$ZsIuBQ}U2zd?*=a3DdMhR|;)Wgo`4hSUE!jYxw!g1ko3 zK9X{cV%Sl18%_VwN1?_sd>q5FZ=jB49Ai&IeSRF&aUlfdd4cl1kPUTwG{N{@Bpv$( z>V)Y~Cz9`^+QdPqlT)F_?}Pdhc}~fL$~sV;mIU=>hMmqZuhMPicBrqB_iVV$W|0cuDCy%%2|2E(8UAn)=_)=^H<6K1Ei%GL&1aS*0>mqd-`7gTym35H1g6^qN zL?+agLkY(9KHWYb%?D{v(|n+=qJ32c)DOw`!-G&)Cqhl9TY3uAHMI%yS?f!X=A%f0 zG#Rs?eoViQFF{?$c-K#dn(0SSmJLZzH=cyL=^Rw{A=E8VL>APoOQCKfukC42cQD*1 z{h;nT3w2LtsC%=a?x)|UjiDaA0`-Un^)t$LjN!8wj$;`0Kzbt&!Iin8)_cu@=0$+5R9iF85*WjPe>v())kt%8Jczy)b0h%4B>^pr=dBr(6NL!0MYj=iLCkEQ%q<@0`PcTeyGPJtn zU#}&!`i!rBI<%0X#3g8<-Jyliy#eEFK>5Psp*`tKoQ2kq;Tpw4Ya9rzNo{CNgNc*S zo=Sig;SG5mx7J*P)`EU5u0v~?39S`*waSCmn!KLg4lQyiv^J^G+NMBj$9SWN_IIFl zWPH&<(4JxZon}FMmVCSTKV_&$YU+tKB`Szf|e0XFzm)DPNbnw!aE3*ELb?rj9#+GxDM?=G_+4cpdBQ?Lr0+-ZgLS>%^Bl-NvU zL;IX`pY!?iRDyDU;Y)NUGN7GkOiUnBpq+FlDAP&Ooh02U(w!pRsbu0hv@c2bWhQYN z+E84EpNSySp`E4w*;pbC-0VfPBvOex z(7q$hcW0q}A4QPYxuFEj^N9pyx)4O{gLaYb7s>Zx9<)nK3G)A;F+u)6)+YK93yE9M zI5(qR-VE)^1cGr~W&BqYp#9`SBtyHF2JJd!xK97;e5aq?iE%_0v>Qo8Hnd;J^B4MO zdlL(x{mSQGcR~A&=5Hg2qtI@pL*p1v`+YjJ+kVjgh=g`0724f9&~oXX%eeApLCX(; zX8982U$785bxr6Z8oCllP>;9TkJtxY3xcjkK{pbhnCj7(PsyXuOQk^fAa9RM=$_mM<#`3V7wNpRpqCCOwnO)3Snuo5%g`*74ZZ9* z;wiP(?) z{7ypm_aKHsuSx{8B@bm(js^coBwM4F&m&}&8z^m~l-j~#?wE0(we zy*AC-jH^xpaRvJ04EJ~{bha1z6BCHj(1T+Ly44LL$gAE^==FUF@(ZC`C}~6aPGJli zmIl3nH$i?4&Os0NCB_kVpg&0&p4+f{0jRJM`A>L=5z&HDUzxNXFYnf_`jo^ma*L_Cs%P zBgmsecOn^jM~3SdL!?8GW<1doh&<@ekfsy)JnKW8hTfTRb*T-#t1t9!;n2I&tw#p* zUYnu!&VtT9sNVMy^#1YC2QGy^I12iZThNEmZ3Ov_Yzci#8g%vz^~s6Q6a0vN#6IZL zG$N8X2>s;K_IHZcYI8>D+<96`D{-3ijoC9kZ+6Ert59NRp7Q#SO?;RM~b#6sUj_ic>{rqA|7 zg7370?mOwWleC|tL*GRnyFrjF#9Rb0m|~J50MJ} zU>fv8A<#Ld*N@WuGrApTyjk6$f4&g353uFE2qq9Sr?z`hCN2XRbs4HV*oC zC!wE9gnmJT&UQrqp*Hj%>3{hg^s9}bvu)6?9fkfg`QO+F{g*q?Z<6P4Eur5c-EH!_ z!!UPaq31?I&-aC1-~$8QVTkK6RPM{tvS1i|Hq&9)xtGN)0)~4mj1tK(N*;va;Q@nn zv*DEhqx4Z2Wk^#t5k@)sm3N0xAsR--co@F>U{qq5${8@K(C$ampJA%Dgb^?lMqn^; z5=ON&7}Y1hs1Zoqff2L|Mosd4EE7hpTQF*8!+1On#uGs>f@>4kVbrC6J%+1KJ|Q76 zLTQFEd;|K2lh>2|V6ZQ3G-BAs=`fml!+2^0jEL(nnq|Ofeh@|ry0_d6gY)Y~>l7GI zABE8-f*?)XR2c2}j!{7bWopmo4&z{Sr2NsOc?JY|bV`EpET21j5DQ^+WjtN4!01Na z-ABOa!RH?2+mkZ&VjMAzVf5Bu^dYalHiEMBBai;sFa|K(z(^Re1W7(=(i7?ufRIQcy1P7H-Hg8W7>-jNJD>K2UAi7?_~VT`qbi~a<|j|(C$!FYk; z#>c>T(HF)9hMP#5iE%I{k#5o~7?U@{h^JdTWqpZpzC@X(Fx=G6U~^6!gfXopaTdla zjC1-)7_X+mm_hqYhU1)}F_ZMO0*P26mAC@qwGbj5#%%JLodV-^%Fg=HNYsc77;nT7 zM`6sN{~XfIxddab4-rL>N77~(Z=QrPKL|!L?Qbn5&cRsV2jlGo;uegBLt(r_8Qvk^ zcS!s0EEw;B;B$%x!8lTqi7PM`CBj%tx5b@_r7)Iw!&sUIV;SRS-DWI52V(`}PW2(O zV61c}h7y@D-Y3uZ8x!%wX&4`b5DCOl7-@W;G=^Q(k2nZaugW*F-t3C6YFk4T1*nGIv3FG0FZ9>b8dm~`%>kMN*U%pDkK8TMP!d`JK9$m{!X zf^mL-8pb(>J4gFD#(AFnE|Bg5d0b@N7wLZ~me>d5hd_e%A5vlbIE$d$<)JXHknRfY zR~X;b5aKM1Yu+%f`@#4b1bN@+2P2#IY|>_D!MGU*Bjw|?hNam46{Tk%#uTiG=jWKMH3k?JrZGhYD9N}@p}aljDvHlrZ@e{#K0^| zKlZsz_O(r)B$(xch+8l#Fs_QzVfr$R?{%1!$iEWZE1!m0g)~(r5GP^!1ry}u9}hFY zhv*D5FcoICESS}4*3gJp;vmc*%3d>?V0@1;e62W`wP(Ssa|PxT4EF^22a``QY3uqD zyI|I%SuY7@ebUxvd?A4ZX+pAKh6WP}#AbprhB2P7RG1AkB9b8AaE1#%2lL5jg8UjX zOv5yojVNoQbeN6FuQ7Qv&W729Jeuage2Vc$3?(uP(aalWvlQYq%;p|MOM<*wFl-CH zM+>^QOoiFXk4OR!?GmJEJqza3q>totB>mfvPaDSFmT|Ye0<+y&m{Hw{TQJ+l5SL(f z2qBij?C4I=j1D3w=QBfLcCr!YU_P4(vora3nFX_JV}fqoM!@XOxVqD?M?ZpYJ*UI$ zMLXLnGbRIOZ}RR-nttBIE|~pK!W>Ayf%{;_MiC4%C=TXe+K2cO*)WGP?xB3YVRReD zFvA&sIC&0dnCB?lbIHUVm?J3P2>OpCzmYUY#lsx^|8@2ia9UK||I>G7L_`S#)CX4# zSn!$HKv7}$c?J~&5p~zxr36J(K*jFD?(Xhq!A|V%?(Xh*jsJJ<`OdPw|M&g{9hhjC`FYN$D=Gk1!iuzsn%HZauQpu@ZherUfBp6zoPem@!t zu^+YXgNE8~4gB6T)cz zw;AfldP5zxzoCxqFw`-t;)iGRd*O#Zj>SBVJw5;_hcWKKvqk7@(T|3@4{h$pSPwe* zq2Gr#Fx11iet367J%Tea1z`?ML(Z)REc*8Jm>fPUP z^QXqj=Dr606fUwn4PzpHa%~#Rp(NLq(HoXru5F_cy~(wMIyGLqxc?}xJ>36`*UFe^ z4&b$qzpT15uLGlG=6}I4A9o2{pTg^2xL&~PCC!rgJg<8jsr5at`xqhK6O*<}8@=qZ zTw^i=>`}ZnjX}tT*L6$_e-C9AuWciBdyt*${b}b8$5r*W>Z;ZpO|iP1Y^O7<&Rv$GzFc zoPX5Z1a+pM?>&uOjS0Bk30E_XPSji6-*BU6t+hFJWULJwI*}F`0XCv-*_hO$Rp}qC zw!-s#>>0pe7BEbV5vU*GotSkn<_usr{D182pExYWt%NUNZ;1Xn(d(3dqrwb~mp>_? z_t}6`fWxkUvyEMFZ#QEa{>^dP6~7VPR})YpzqhAuJx2E{xX;nJ=RBATp3K4g@zM3} zb|cVcD$4(-=Q+OwvrPAwb94)h&0J$|ja|-7sWB69GJ2hb(Q-bOfK@(E;d0K=yzUmD zz{Y@^0js=j$ynU;#{cM{9?$-r6A|W~|3s4h)3^mToygGdM1Q$_Ov1nU*n6X#;~{j; zj4}8$O@2KW)G2k-LMSx{bIIj+cRbB0o=d|_z+5VK0_8RX-W&gawOK3=JtcK9J#q;b zDVt+da6JbY;_naWyL)PEMJ{vQnG71w#!RQ8RzCAwt9HXxu2Dk$67EgaZKtCT(W(Eb z*Tq!Id&v1cN7HbQ=3ma$ebHau{}|jC9iFAJ$|;=d%hVpcou=`a-Q!M9$9X-dOkc~Oe z&E2(3{u6^4{}?OBDwhiUofu7(+!Evz-(8Q4x0LaGD(qmVmYZDKI#I8LtoWW7&%x82 zZ(HcO?}Qpt(a#*+Q{KZqsErR5V&%erf30o2E?58j7i*wlnE2cUzAlFEV0fm&pE(Z9 z&|Csvpj;APx?IZagHM<(WA-)s;lrQ<@R7jf@Co!4%z@@0d|_-Qb7ibWgUwaUI&)QX zh`E}%y19nAra2UUjJ)0)j_+KqWtQEVk7ta~X<_vRZa~E@_xvMz~UtQfDUniMu&cPQJ_cZr1 z_crI5`{26*`{C=Q2bc$%2bl+(hnR<&hna_)N0>*NN0~>P$C&fYW6k5tY~EttYTjnvZr)+ui4VozZQf(vYc4YHGw(MaFdsA@ zG9NY{F&{M_GaomfFrPG^GM_e|F`qS`GoLqKFkduZGG8`dF<&)bGhfG_h z$9&g(&wStf!2Hns$o$y+#QfC!%>3N^!u-xpcm5p@d|S-M=T=La6K8QBm znqlp1?PATecC}_%yIH$idswrrIo4cjPirr0Z)={lkF~G0pS8brfOVjCkae(ih;^uS zn02^ygmt8Kly$Uqj5Xgn);i8Q-a5fL(K^XG**e8K)jG{O-8#cM(>lvK+d9WO*E-KS z-@3rM(7MRF*t*2J)Vj>N+`7WL(z?pJ+PcQN*1FER-nzlM(W+Tp)&gsxb(3|ob&GYY zb(?j&b%%AQb(eLwb&qwgwaB{9y5D-hdeC~vdf0lzdenN%dfa-#deVBzdfIx%de(Z* zdfs}$deM5xdf9r#dewT(dfj@%deeH#dfR%(de?f-df)oM`q28w`q=u!`qcW&`rP`$ z`qKK!`r7)&`qui+`ri7%`qBEy`q}!$`qlc)`rZ1M^@sJR^_TTGzA|ItOVBnxL*v?> zt!&@MUpcUsuzT4{+P&?i>^}C=_A+)~yPw_P9$+tPFJ~`ruV4?f2iYsyE7>dCC43iQ z6}!$})gEH6X0L9qVXtWqwTIdD_HcWIy_Q|JBRjSeJGC3^M!U&wwny4)+w0h)>~-z+ z?Dg%@_87axZnZ0R)z0j(_6GKb_D1$Ndt-YOdsBNed%V55J;C0>o@nE3JbP=q&2G21 zvA4Civ$wZ*uy?dO>`r@VSMAsA*X=j#H|@9V zx9xZAckTD=_w5ht5ABcakL^$FPwmg_&+RYlFYT}FukCN_Z|(2w@9iJ#AMKy)pY31l zU+v%Q-|c_df7pN8f7yTIDA;r?eCfe)TznWqIldD(p|gb3%UROt?JR|)o28v)oW4#! zr@u46S=Lz&DK{%P1D!$6iq1;T%1+4{?5yI{IjcHDoYkDwoi&^_ouSS!r`{RvjBwU+ z%1-3OPU56agVX3VInB;UXKiO4XOy$9v!1iQGuj!0#Ftj5;#8f?8S8A|Z0Ky{jB_@2 zHgPs}Hgm>1n>!PnEu4wYmd;kr)=rz#?rh_1>ul$2@9g00=yW)p&Ln5DGsT(eOmlW} zraLp7ot<5rna-}xEN3?)pzPtycIG&9ojsksoV}fS&OXk*4*rsibAWT8bC7eebBJ@O zbC`3ubA)rGbCh$mbBr_JIo3JOIo>(JIng=EIoUY{X&|RLr#ojjXF6v&XFKOO=Q`&( z=Q|fT7djU?7dw|YmpYd@mpfNDS2|ZYS3B1@*E-iZ*E=^jH##+^%UR$obZ&BPc5ZQQ zb#8NRckVzc#$C?c&OOe(&LZbN=YHn_=RxNo=V9j&=TYY|=W*u==Sk-&=V_!-JnKB? zJny{Vyy(2-yzIQyzac=yy?8yz9K@yzhMAeCT}SeC&MUeCmAWeC~YV zeCd4UeC>SWeCvGYeDD0={D^dbpPgTvU!C8a-;o0Fhx4cNm-Dx4xTb5lw(GcV{^?HF zcLO(cmvDQzOS-+?rQAO5((W>DU$>vz-yPsC>n`Um@2=nubO*UBx+}RWyCrw9yNX-q zuIdhPS94c)*KpT#hq}YudUv=x!d=TPyOA5aiJQ6&Zll}eHoGI;wcT~xQSQ3#dhYt} zXm^a;;uba!=Uxx2Z$yL-5^-8t@DcTaaO zcW-x|yN|oCyPvzidw_eOdyspudx(3edzgE;dxU$Wdz5>$dyG5ZJ=Q(WJ>EURJ<&bM zJ=s0QJ=HzUJ>5OSJ<~nQJ=;CUJ=ZR{+z0keLz1Y3Pz0|$Tz1+RRz0$qPz1qFT zz1F?Xz23dSz0s|?UG4&Rp?i~ivwMqst9zS!yL*Rwr+b%sw|kF!ue->-&%NJ$z#(ma(&VAl}!F|zv$$i;<#eLO%&3)Z{!+q0z%YEB@ z$9>m*&wbzh!2Qtu$o<&;#QoI$%>CT`!u`_y%Kh5?#{Jg)&i&r~!Tr(w$^F^=#r@U& z&Hdf|m-~nNr~8-tH(s$eJNR+cUX$1Cjr7*`*6~Jp>w4>X>wBZUFQ%g|mw98o4ZID#jl6N*#@;60 zrru`WcyDuWg13b?(c9A7%G=s&^V+>_yluVhyzRXmydAv`uhX04P4=dEQ@v^4PTq8H zhPSh~i#OBT72gQl&D-7E!<+5R@#cDadV6_$d-J?~ynVg>y#2icyaT<1yo0?%yhFXi zyu-aCyd%A%yraEiy!qa--f`aX-U;4`-bvoc-YMRx-f7s_quyiQIi{4A#%ib&AtKMtg>)spQo8DXA+ul3gyWV@=``!oMhu%lt$KEI2r`~7Y=iV3I zm)=+2*WNeYx88T&_udcQkKRw-&)zTIuikIo@7}+>KfFJ^zr4TkO;uA_%EmY6T;(aH zd=;orEung;B~@>=leX;HLan9BDpIjZRH_Y7aGA%~5mJo@y_(x0)c)!K zb)Y&(9jp#fhpNNW;pzx=q&i9+t&UOi)v@Y0b-X%3ov2PyC#zG`sp>R!x;jIhsm@Yo zt8>)3>O6J6xah(OVp+6GIhDSLS3n@Qdg^M)V1n5b-lVl-Kc7+OD#|f)lKSV zb&I-H-KK6=cc?qnUFvRikGfYaQunF*)dT85^^kg4J)#~}kEzGi6Y5FzlzLh{qn=gI zspr)T>P7XEdRe`qURAHD*VP;9P4$*~TfL*+Rqv_y)d%WB^^y8meWE^9pQ+E)7wSv( zmHJwJqrO$&sqfVf>PPjH`dR&=epSDz-_^g=AL>u_m-;&oKKhn#`;PD8i^|IP{lE|X zCH!9gl74T0DZh`uw7-ns*YD@|_XqgP`pfyt`z!bZ{XzbU{!0GJe#sx~uj1GFtNKIy z)%?}{HT*UGq5d$x-XHFd@YnLoe&ok~;-`Lt-{?2_&HhM#ZGRnql)tXOp1;07+8^V$ z_^p1$ulkuk*5AP2(BH@(=Wpz9;&1A2=8yL`_b2#U_!Ipt{jL11{Wibd-^Sn8-_GCO z-@)I}@9;bQN&aMiia*t#=I`WB_hfh$y?%(0x>EGqw?cd|y>o4-}^Y8Z`@E`Oa@*nme z@gMac^B?!0@SpUb@}Ksf@t^gd^Pl%$@L%*_@?Z8}@n7{{^I!Mh@Za>`^56E~@!!Q) zi{AG?@IUlF@;~-J@jvxH^FQ~$@W1rG^1t@K@xS%I^S}3h@PG7w@_+V!@qhJy^MCjM z<^SR1%a#7$_;#)tSb-flfg5;%3VeKDGYpo%H#V0HdIw7deS)QfWrDszzo36GAXqk7 zE?7QTAs8483RVnO3RVtE!Qfz(pe|T77!s@&tRAcptQic&7r^R+;lYStt)Lu4K^!DO z8Z-orK~vBij11Ne)(J)h>jvuu>j$HQF+oev8dQR6kOgCd4T24Wje>E(#=$1Rrom>x z_+ay3La;?JG1xNLD%d(`3)+Kif^CECg6)GHf*pg7pfi{hOb(_5Q-f*2PQmnGMzC|R zOE5FoHJBCb7VIAE5zG$e1apHugS~>igL%O|!M?$M!T!Mk!GXa+!NI{H!J)xn!QsIX z!I8mH!O_7n!TjLZ;JD!U;Dq4B;H2Q>;FRFh;I!cM;EdqR;H=>6;GE#x;Jo1c;DX@7 z;G*E-;F93d;IiQI;ELeN;Hu#2;F{pt;JV=Y;D+GFpcZrm3xb8gO~K86Er>EM~*+2Fb0`QU}% z#o(pj<=~az)!?<@_27-*&ET!z?ckl@-Qd08{osS(!{DRf)@N<+u*z4`{0M*$Ka>n=iry%*WkC{_uyZ_AHko&U%}s@5t^YD+MyG=p%<#q4}%cj zgbI6wONPC}rNTbp(%~{;->_fUKO7J)8!i_vAFdD%3ez!*HW;T)1(#Nw{gaSvWr2Je&}25l#%Z47Uom4%@=^aGP-3aJz8( zaEEZmup{gYCxw&4DdE&`TDVg$`ctChycu;t7cu06?cvyINctm()cvN_FcuY7yJT^QoJU%=jJTW{eJUKii zJT*KmJUu)kJTp8iJUcumJU2WqJU_f3yfC~dyg0lhyfnNlyga-jyfVBhygIxlyf(Zp zygs}kyfLhWUEzXoVR%z`b9hU5Yj|6Ddw559XLwh5cX&^DZ@4JDFT6i|Abc=DSSD6C44n}EqpzEBYZP_D||bACww=2 zFML1zAp9`=DEv75B>Xh|Ec`tDBK$J^D*QV9Cj2)1F8n_HLG3wfM!B3u0$WqUgs@Cl zJdas!&@jrzx(+x3a9qiK;DZ-@^7SY~P~Wmy?k)ucTS}%gWMURvxMQ$;#4iR&HTg z`p?RGK2cVdak6qI&s*5Oh3#8(`*NJHpBCK@$Sw0vD+Lt%vSzk#X8UHgYv%aP9KV_4 zH*@^vVtoA^^OAAOtuoK3O8#W>Jgtz=Dd{5g2i-}Zvb2j+k%OpN=#)m%Pcl+qx%+uj zKL2vLRba&SkBNl!JaXtC!lTzsK*7=`~cMR2h{Wjq<*C2m+%dE2%fRv1+^jF z`qj@tUYSR^Rqj`1{Bk7o z03BGbwSc-m=#}md^wf9&7W9&OQPEFAzDxhmBdH%3dQN$)Qa_`S1sw&ig!Un!{m&B4 zJK?+wyDN4k$`U<3Am^QM-U;VjVfzZ(S4g)C=}SFNyX8yw2YJ%{0qXt$b$?YDuG}sH zYP|uZ{6>^tu@ksY`4xMCGUZqF7iEnPpvDJK;{(X?h3_bH{D|WhdJ}Vek!LK|9AD%b zWsWcMj55a;{-MnAg^wr;J<2Khnow^1KDpXF5QM~Uz+stTQ}!vCl$_5x7) zNoC!K__2SHXV^C>L&5ZV6A7QoQL&DYuT>cb{T1Wrb||wQ{he6qgN(W!WK^t=Tz8^W^gqgom#lM9RqQBWale?ij2Ds4 zDc7G=$MCVTq^h4vqpOp4dwTbi+Jx7^*YvR1c9zd>1&%*ACeBnOnQYBq7 z@`wI+S>y!$vR;$WsoX$3i}9OvJD7Fyv6*;rUsV-<6#OE7+}Bh^udqA_JyPyZ65%uI z3%#Qjk*jirc8qo{lKEmeE5x zR}sC4#a}>s;zPX3giGf6yvpK-pv>{b?x4)^#qYp&oa2lCfilMz{Y07Li~oQ!$Cq^qWsXn(pOG6zm#{zORBt|a9^GlaVOgC zNc?2Dqq085g`J_Dh{caZzm#*vWvbo%^S<FThuS0*N-#JRf!`(pQ*1coOi317x+cwqr^Q>7XG9nuMiut^C|sx1JuF* z<0QhEGI&>RBvZvWKnO^DF&^k&KSz5iH#a{qor7}WoK^L6w3kIS;-)I#=$FC5 zm`W5E!Y{!FFqy_hoKGx@VkUI+ z)pD_!l+6jsx<5ebtvD?xQ*UKmK$&_g>juiyTiW$dne5!auHbcl#gL}xrM z7HuhGQ>*0xko1@FP}cLrIwt&#V?KJr-fvX@Qxd9C6`+F`^c;len42 zwpbMDxkbgISa=j-Oo0rAS(F-UF@>3t>N16{%$-?OEPisbhCsSZBIb!pxj4aLWUMTn zJa)oVY8t(WMkJ!}MTHS7B20`-MMXr4JMM@(?U98R=F; zs<`N+ROVFZf&ii@Z(4ZP+(D;wW>Pu{sVoB65exohiA*4XO%K0t#%PRU*%2dB!bMz^ z8)a=wfOjFEoKNBO@g%p*6Xt{`O$m3@Np}QUj8Z~lmvBdr$cZvGYO;tW-1%md-=sKU zrBj-T9^ha{;|VD8l`+zkQLi#aoHFWHCVGK`b3G2A@F6QsFp3BSPqH)a$TM+fQD69% z75XSUd~77eDNeWpP6|EZ{8Jj+l=MxzBiGtEVxvhXw{Uj3gGnWlhK*$5ylA?kzUXsG zCnM$JoYJXJ8No=46EezYN+&!e|5G^uMFdFropQ&R^5iHjoPS1)Q!f4~BgZM7;gl!L zDV>6p5#^N5L0Zs(d`@|Co08wDoV?-yK=_|BLm{QzO_`xkrd*dZ%27f8f?v#B$RuI` zUs>}7P~SA2LRO zGG;(zj09!Ogvf*sQ6>>11Slv+jJPG_TT(=#L>_SalGD8_VdW7>(Bi+9G8`HVaA zj1lIHi+;wF@{Bv+jPjHe85#v%h5XBj7EVaT4rDx`&A5rncv72jGnetiHshu)W5hCJ zo-8BljscJ82O#p?{Uq??`!4k@6%e zWiTwIy-X!ShnS0&3j}EC2XTEYGh?Jox|E9urPw3Hcr-r%wLggnrJe`M+HU~V_67H~ zy+K)zgK|-y^@<3m&=K{tJffbKFO;?2N15#ff0T>++D@RX{SQDr55NLH;w9@T`YY;- zTr!w}`}9v4xo3nO2|cpzL`9H|^2|s(o(tU~uG5UT^Tno4`zL@JFF;KX zK#dom#-qy3GN7I}pvD7G;|-|k1gOUc)bs+>;{s~>19DukQz&!%Vslz-T8quG>>H2> zC+h(eTW^MObeTgOl!&}`^Mi8C6M~qLen44YNLREIzBA(t2~&JdKdLP2HQG@QWt~Qu z<8w1#7JQKrQSPNP`{L#ilMxYSVPua&Lrc#>rAMKu=aZ(MPtvlk#wm-4l#4@GnvybI ze~Ks$14$CPc-7H0VQqMx#qY(Faf&qIxNqcq>_bjBXJ*)K?6qx8IA`xyY!5}LoDVOzltlmeQ?ulK1dvil_9TK%w2IdP^d|9 zR>6cOoG*zPD$`9WbJ;D-YFwyWQ&nUnS|hWfk8BIN!uMjGK79RPID;yi@AoZNzG!wk=~vW zTzFXnc*+GtMe&%$iL%%*WM%2HAf#D1Zg)3w4#9X0gICH!}w6 zS}zzzL|L$6co_5(dFPoMHYmCrRTyrqFw9-yu~tP`i|1Ojq6$5_N)hf9ym53V6pOi| zs?Z~d>5*2%(?vav4^lfco`8BjNC_5m7t^DO>A_a$=~kE{Q7Lv-RDqb`<(N6c6%loe z!xe=3$(0;*rGCo_hI%xIvVx&ZeU}vsWvY}|ew4XVie~}7(9@6=4$n1T0BJsDXM=jW zAHagItS6o+?(6!1v`^e&RmAf~jziHO`7a(TPQ5t3M6OU4{+DGZivBddQAOyB=f$|X zKit;}Oz)>b$We1Kj=_)&Llu1{K(4tJbil>P( z=_>Mr@rwRLUd!T%qaNubo*L>AZ`m26tj7V=bO9vY3w;v#!2O~h(!0nV5DyK{H64&r zujL3(&lgbB7f|B|NE0nPJ(NYBne&e_@sJ%B%H)4>EGfqzxUca5Bpu`!21ocDUv{o2 zb9{+tq0I4%91hvh;y&phksFjr2ZhL#N!vz8G5JtMSxoP$zuYPi~Ay%JVwBMjThEY%8_`+SmJ0!>6Y-F3Z&{uJ-%y$ zve+Yf^hjyZ^u;lu*o8#o6vn%GsoaB`GGprOsdHw`88La5UKwKU;NnVaul)Jk88fF$ zEgn)y&?%YA#G051YH?*un7fC7Bleuk{xm<}kZU|a58V`1^t=%u)$)WC8^Jqa#4q7H z1_>jH2_tj~bBGh>UMGxXCPgj@BXbFJOcUl9CCsr%7#U0$u}tU@CyYcUbSD!=3=>8$ z6MEzccWMd!nuI%*q}aj8N(%aFdSh9lU6CCsUUA}mnJ>z;TY@LbTCM=ef0-xBv?DTq zlxas~J}6U;BytNm5Ie%$8?3^Vzv8%*_LI5yIIR-AnzDv`m2T#;6^R<+Fi*1$)dbz9 ztWl~-k%zUYCRi~uAJqlxs2GjbIuZvD_4!=18D(NAs{`6|7P2Z}5oiD6l;>D4c6Wff zE-WGVFMfbYEG}Rsiwl^};sWNR3(~&F1I(^xt;M??jNikp!(T$Mw297iBPF(x65B$^gdR9*GM|Jy z7iuPwyrrK+_P$8oEXp#!gezb~dN3w{dNNOHCd#Z&IxwaeGiwr%vP91sY0kTO-#z;vvKS`^UI6_rQnFgthm&$mljF-xIsf?G(c&Ut+%6O@aC;Ju5 zLj0MCIiyU7!mEg+26IUHP73amsuBr8nN*d15z4aUM@9bADGIH}g-Aj+fL zAw5UqgLFiVA9mxK`^bfr=ag>L*II^fi_}ZGU*(E~bPc&5aes!?0qy@p^mkHOI1$*> zRvss29H0I}%y$G*W)>toxlS1oOpA90c#=fEq8}~o2=izu#;aN~znJ|*j3`r!5h#>; zDZ^>0?CWq}@Q*oO%y)d_;^d7dpb^uEQf6QzMJ5K*FH=SqBKnzWk+#I3LCXDpB&!|5 zP@2!+3;n{P|AHP&>!VdcU{dO(Ji$*3I*FCUiEV4|9ktUsdG>B|3b(cziMv;g=tjn3 zVoJb#Yc=TqIN>QgaPq{+a|t}jCmKvRvTkP@i^bY*m8ae zBWDQ@))P?&Y&$f$F>cY0{Ylsn^wj+VQauZ|lnESM%)nlmcgk%aU(Er2RCA#>76F-0 z%oP5ZYL$qy0(v&|oxIzwyLNU?o;-Ed+}SgBnm(7i6SSoW@sK4cUXx)IFcHTGxRTc5 zoB{`$o^s1lllPfDW9G~mljnBMn2COScb8{OE;d1Fk<(LXIM0ld!h(vZV3=ZRB#}%3 zXcoiKLW)0x`r2~A0?~qrsNud?q*!*kNO2NY7VB-18GG}mh?XlZ zA{F8%!h#fJ5fMWYknG^Z2S<*M_9FmkX^MV|8~~ni#q>8KMw*ITu9y}#rX`6fiYfa~ zxpR)0n-TMDF%rKBp~%8=Qbc%09gE)v6%rARc@~!xIYc~rOqkOG2Tso`p-Ly+k#cPT zv18NpMTA`HrQG4C5@g2NozN-anMqpYRMFxkq))=!5c(T9Vi7(j;!onJChE7#ZnJlr zHDitl^USH!3R#9-l0niUF;12X2m_f?9AMMlB!%$sU?(cFFX^-vuO!69ftO_KR+*&> zq159+7xbiYUxcL`(pRyAufX-BcL`SfMUP-swK)=c>pyYfZR}voT03h3{d9Loy|(OAubYt zcz_)j2jq+ZrNt|XJTOmqa2a#+5i#7%L`cXJ5f65wVoi{B5B#A~mQ5+vOD#9h4^1yTryMbnA}O-3 zi&v*;EMm%eTtvwDs!&{HX;VKUF~aCy<`eS(K4LhX39&FnV)T>lV>*#9WWtD%ro3Rc zDrK(gG5wX8VXT<+j%mzduInJYw4?i9Wa-CzYa=f7y;xL=uszrHn6Ey?#Vb<9tKLP{ zK8;~q=mXydh>L_#y5(`vAKxa3i}@AqArnz!CM?EGY>AnW6f;32W34 z!UOb#2_Oj*KoTZ~Bs}IxidRDmeHOnA`3FL;go{=}dz3JXJCW@R>XC0;loRSpQs@Vd zb&_InU^aQe?MuSMmW11egvUim;s1#qLr!H;Oql4M&`nO5pqP9)@WLOv(tb3#5RMWQYF znG^|@|Z@A+KEPIiOyT05!h> zwS5KD>k6Q@3xJybfSTWc+O7d=y#~}y6QH)MfZBcoYJCONb_bB-i{Benn7CJA;$DSs zrBozfisu}k34IlzH|}$Mp*PALU+gK$9ABJGlsUevYlwYu{>3q7aV#m$51w=WV((Dq z{Fz8!;VaPN_2&Z^%cHqUSWcLMVx8$!}*K7K=Hw zc|n=;Az!Qf(>7K9Nt-GYud5zw_+-U#81czOF5s83J>?+dW`Qqc;LT=PznGAWvaS!m zQTUmWe;MhXk?xt$9rc9XnT(6F$bBaC!1!8@;NKKbkB3ur-9Mmi52)J#ia)})7ja+w8NNjXJhc4bSeLLkj+bx+63aAS0O^m3+#_hK`Ha6j z!f{2;@D~*{o#0=}bH0*~vdoYEH2zYB%#(it4P}`pU!6u-@Zq|SGW{(c14ldtZlYga z5q^$o6uBJJ#PUcvenLL09uH8{6;O`{sObu*#{<-K1=QmK>iGlec>-!X1gPl?sObWz z=L<-`fqyC{<_iNU6O+>-ST5tjAJOAr)1})ZuT1v`H(1jVex3A}a=lK9uoKsxl7o6btvcI(U_~$r=k&gpx^!R$535)S5`mtyJby3f; z?EU{biR0<9i+cW(j?r^W|4sYbp5xYx`cloX3pju#9m8v$CO0Tin$o2vwUqLQ`E&;g`uV>#)lr>X zbB7Mb!`9OL(tM27Rd$DfLYudBj33xJp}noHeQ8x3krq#^JK%}OI4zC6Er5xMdn3iKqLKWaUx}!a>(oxaahV=`A z-bQP+W$55;!C9jIpMum&dNGFqg;q?xqg0(=*O@a_6WbV=Go)4;2qX(E!P>gciVU&j ze`#J@6|K;?dulx!_0HwLu4Unpy$rhw77eT$+&&aSyi|RcWmRiaIxE9#ed;lVQmM97 z>qhy^Z~+e0`sAevDD~0&S{fbo(L^bM29tq!ZRysI()^B6ZE28gcx{>baTD9RoGF#| zRclL5t(!Nz*0+A#=56D)kedSs<9=U#zh8Zqu}tfhZC%SO(^@k-TWU)W%T*4d)zY<8 z{!btLQ!@vE7xs|xZC$w@f?O^0!C?&BXXxNMG%Kzl5Bc-F2{bu>+K!ow1^#1k_n*xA zFD&dbjJ|asa%;_4Z-Hr=S|0k>8(oG~o!C}ermm$_t@VcZEm;SVgDCdHoetn|>vGGP z#?nS#qot)Kr$j#>f@fX*{9(0YhYef>ay|f)?LTaIZQ1%RGl$E8@Ht$*zRS+x3iVx1 z4hPnExj7tE-{s|S#riIl!-nFuxquVSYJW74ysC z5X>)!t6_dQTpjbv;To7<4%fu|ayS(8%i)On(kQJ_Yt@6q%XO4s{mhPBI647qBXZ#_ z*Von>R$B{-89^t;LUjM5X4Z8!*X0)ef2;wy9A1kHUU&8Os=4UZqM==`*}vKb>zH#f z=|P_VRwu15HE7Hm(3|vA{om~c%+PSeUlmS`O|b^eIDQfbtD^tsMItCdDbe>D%PA3!T;671~>JlN-W%V|-vxmvz)UaQmNn3)M1*$@1j1c1)?OxmaCc z^_Y_rL1mjK4{Lx^@?YDGD7K^ zF|ln#X%trW9D7S{qSJ0x)Vv`mZHPzRPK+?|f0w+DI&uq3!0_7o#b}!PWLHP->HPbY z3l@)t{TPu`F{>@x+BSY57T40K_7PoWvmf+!%s-w^7&!hPPh0-+v}oJ2zkhGhT3=gx zSkDSYqhi2HeQlj#^C5n@jLnCp|2N#g&=IvVP^{`H=VH8?P`(qMT8m6Q7w0-?+6ZW# zOem}G3bE+q(yt}||4pRF{wD$Z|B>RH-nktcRo6Ula1VJI+)g~l!T_&5tf0yUD6KPW zaL%)wE}Ts_2{r@?`it7bO@W5@9Z_q9mT&Z5?vF!vX1~6*Ce+!uzBUqIlbniGkh_#& zH7=;NX?-rxwM{_g&FU8zMuv;=xG?jJ&FdGK`pyJg=sR2Fb;jb+#Jo;^v1MK-zt}3T zlV5CIzYw;t6<2Mz!Xl5W_WFgU+}j3Ma&Ozbo|!-0F0ZGrw$JP7s~z%s`fA5~%qpsN zyz;AEHD3AEER9!wwHs(Ws+)nk>r!nr9_%5PV{kb;CwgA3)`HR;thKnz z>&%tQyw0AwPW~XTvlkk#+uiHlx};molgqrtK605?-xvL?D_!m_jjaR=GBjq%e?y0 zXt}=hcZ^)-73RxjUg6mK1xsibx8@C8;8=DQyCiBuZf<8Q7VzC)~ zbOKyU^8_q+vi;k=3tz4u9h`l{9@}rS?wZONs>^+j!&g$K_!IF>rn0}1QXy}jfe$6k z^0vdL+1B<}clGVwEn|YZ(JPH!esurQOONg~S}nrow3k5r{&--F{>T6MgWK`lXQR@! zs(JM0*iIbXmY1hgy4J|cxA}(LFe>c>yH?L{-QgdM&qt3wdh$eF6A$wLM)x@ZpKzJx gZ-oy=#ePYt-a~IQ|30GT9FIl5ve2F48HgtQFNB~r{Qv*} literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/assets/fonts/Inter-Regular.ttf b/mobile/apps/locker/assets/fonts/Inter-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..012d1b470d92db48d6f45478f9711f088a6c7359 GIT binary patch literal 680240 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�e)bOgP`M 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

    U3Dn;hkPLp)Zf$NKy4xR9o?ID;D)_V?47W8 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

    -9tD> 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⁢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 LH~qEeFz3TM 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�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

    $W%!KzKccw$`L<(J;o#5=G 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�cT-# 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%0A#iYtkm8@D`eW89r_55?_^ zdoJ#qxa;w@_=fSV;v?hZ<5S}M$B&Ai6u&ZlYy3U&kH#O1KNj@_k&Lvz)_$jevV*5mQVvoe>iE|Q{C2mN(Bk|$H z1BovrzM1%8;%A9pC;r;m=p59!S?7tJr+1#yd0FQTo$u)UVCN?~KiB#7&L_HrcX4*< z+9kcqpe|#&;A1CUUh48?S4Y>eUGuw^b-ktQtz9>F-P!fwu3vWjv1?T~Tek+?T6T-* z7SpYJx6E#XyG`g;+HGF972P&-J`oFc2Dg- zp!=xq6T46EKBxP#?i;$_(fz^hPjvsWdsUCxJsS6j=#kzdtH-z=Q+rhO*wW*%9tV58 z(&OD8XL?-f@m-HUdfIw6=o#5Fu4huuK0Sx^oZEAG&y79r?D zJRx~n^6ccL$?KDMBtMY6FZpotYsv2?pH2QU`N!m{l+h`ZQtnE5A?5c}mKvPeJT*Mk znc6jVcIxKTJ5nD^J)L?X^_$e+JR4I6YlG<2_S7Gdv4CcY1bv_Is{+eobqh z7M|uz>zbCHHYjaD+Um4zX}i+SrEBSR(r-!+OOHxVNKZ-cmp(0hcKXuv_31m(A4uPq zemMQL^!L-xrC&+^DT8DLW;DqN&A?mF8Oa&_GDc+NW)x@a%6Kf}*^HwZ?`N{i;LPTk z;hE0NZkZXGS(#%q^D|dxZp+-2`Do@dnLqZj_loG1*=ts>RlTs zj=hU|FYdjr_wBtO=>2-{6TScJ)1ptCJ{|ic^~vlru+PLkEBb8ebGpy>eH-_6_U+$y zRNs=moBBT8_vOCt_Otb?*Dtf*qJC@p9qjjFzbpNt`;Y0rzW;&#*ZTiFpy7b10b>VD z88BnO!T}EtI5FU_fwc!V!R4RgfwKoL8+gyamj=Ew@V7y1Q1qYygGLW}XwZQ{FAjP; zi)MAmip$E$D$bgnwKQu@)@@m*vo2(Pn{|C~$HDP~lLz-5yn67q!Mg@OI{2BvFAsia z@JE9$4QVu_&ya#4j}G~4$d#c1L!*X{8M&Ibh_dkrPKwA31mAl92~TzBsDhsGg$+jv76xaMZR@J4Zb{>cFV0qg|sj zMrVy4JGyZ6n$fq7erfb)qkkU#*O-Q5nvZEcCVothF}=pD9J6uE{xR>2`FhN6V}r-K z#||62aO|3~PmO(k>`&u5jB}0aJ}zxs$+$h^PL8iXzRUQM@w3L?Hh%B;&&FTNPR#aX z7iDkBelGis?33A_P3SlwVZx9J(xP2Dl|-l>mGJy+1UpijY$f@6hxVco)}h4#Y8!kogQ!pg$Mg=-797v5L6xA1b2 zQPia>Gm0yUHx<7!t?slo)5@nEp7zbOs_7l4kD5Mn`tIp_r@ub^ z`1I2yfhDa=29+!*xwGWG(vZ^F(xs*EmC>>mWtn9&%Qlz2UUt0f%d+pvyOn#&`<4$W zA5)%JURYjUKDT^H`Rek0oelp88tHZ3sS?RO-&l)zXaMqkzx6ax*>)@>SX9vvg zKD+{l@I~W`8vM((G%qf1drEA}w|MK~ zeT$DRslOzCN&1peOExe0YH8!8Etj@kny_@l(&D8ROJ7*leA)J8k1u<3+2_lumN!}M zTAr~yXZfP#yOtkZeth}m<$tXRS`ogY>x#@3qgUjwn6YBfighdQS+Re`Yb(y&I{4Pz zx4yD6YUSFMU#@Dn%CjnK)#O#>t5&Q!y6WRqzpYMQy>#`O)gQ0vu*S8f%bN5xgV#)0 zGi}YPH4m=YzvhKCZ>{-ZP1V}KwKuJeUfX$X#@fMav)2}`tz5f$?e?{gtbK0n8*4AF ztG_OF-PCmz>lUqByKcw22iHBh?#R0H>wa2aZ+*x08SBTcU$p+N^)Ii#yg}a(w4wQi zhz)TYJRACM7_wo^hP(}h8_GA#-LPcC>J6JW+_B-l4SO~m*zo*@*ESsA@X?0PH(cHD z(}uq`+BODnY_>6MW8_Bn#%>$aHum2*d}H>;DH}^S&e^zRLXH)s6Wt;BVbac~+O+RgJzBy`h`sRYom76zgzH{@#n-6Y2y7|QB zuQ&g(rOB4?Epb~sTLx|!vt{y@vMuwstlF||%e`CnY&o#y`7N()IlkqiEuU|>ivM4? z(yfiRw%s~r>yE8wwl&$7yKV2bUv3M(ZS-w-+;(Dnpg8^3f+oAMeJ_ry*Yzi282tGu zIT`p@8aF@LG4185p@03!elz|=ToZB-@u&AD5%@6uFaH~$301#v=zro}kx%f}{Xf9y zjMo!(lPm*TKe^p!a{m!Xd)I#v^GGwD;}(<)`RP9*5@8XDgZr?;$19f3({yKT<<~8XkOIRPY<^KTaG>nOl(H4N$M?VMq|Acv@1^P-rYfnQO^Lv=d0OC}=2(u&1U-a=J z4N?BzX)#s1ywEqHoK47^n@dE#DsN@KSDMHJ{#9qS^s0-r8Oh*Kb<8lDqFu7SK0od@ zF8br>Ytg3v1E3M82OlF6cI`2=OCnsFRbMK1y!KetS-l}?S&gTsh-BmAqhL-DTtZtxW~pl4y<8d~-9zlBz$wO$ir zNs@jyNeBHn9slbvKF7QU=-L1N{KT8z8QM63uFA}S4tU|G&l09x8&`FT#uB@>gLt(2 zi7rfT7pS_d+zG}x;;9Mrch7$d?ZHpi06l|#dj{(U$Nd<;Z^B%Qx&6NYV=KOU4nPxQ zU%dol;-By;!apJn@wTqtgq>c3JNZ9C`~N8Zy{J>{lK9wL;hCO zfx8aok2JYmz_=<+y-w99Mi=bwfVEyV?R2Ll{7tmy{{W05sK;E?i-Xr^G0f^nspf8n z^O1sBS1oR@e>D?Y@bv!)k77;wcW?;tZiIi8c6rqw#uubAdrd&mUS(0<`S=PoSGEVSJ3XM!)?}_;>r|CA9ZO_!sGE9g^$LE}kXeI4S*BFu|12tP-La3G%` z<0XVSfnFrs=!mcrz)75y<|3~XB!I&>fW7%WQX4mJI~bW{80`g2L7E7}Jr1)ksYfaN z?j}KWBm7o_Pd|n|?hWus{c&v+G7V@H2{c9^-bjR>CGCxYq@K}@)Hn8$I-=}mC~H1x zf*Zrb=rC1piaOd**Plr%=9Cf*jGudyjgQgr5Z_9G}fnIhFFAcf8$45g>D+S(0FP((R z%jM;O9?(eFNPh?W_!$y}erS*U>!R-AbQ0S4Ex8GOFwBUm+JiUUf{awuJBZXWp2iw7 z3Te@AqJ3ziIIoXHe9QUyZ)e0%#6JPP;eOD-d!9RpemFsT8`DTI>eHLyLu5o|w=y)(AX9Zy(OXzEEL1=-vtT1Hc-j+oS9QV802pn+V+t0LDuzyn&#DPYcC< z+1ofKp>Ia~F~CEh-6;6Ig8bfuyB&Df0YE><%+MyBjSS6Ib(g-U>OEr_!pnGBRbT1L zar+8dUXV{K*fqT^_>5BI^N6Z9ujhKuWf2zgz@yKr;&s;##_(HNXvmbqMrjm z%TQ5Q-k$dmw-fpWKwWwZn(%!0qugg?xL!_L8Y5xnbOpc@+Z7F?U`F}q$j_KwT5@{v zdUK$iURnv0(+PdTX`;*V*VB7sdo=4tj?fZ@_N>!k$N*r>gG8dD*5fg)YU8 zDEnCwBFfaj7d}Y3`Y<1HC!w9ZztA??2%9#5n`yRXdwo%jEVuNt`Il%VX!DjCWARSkl9I9kk_rzOCvn z@0o?4DQ=jbH$6x)`wpU+|~{QS}QZm2!T5k z`~#r(1>O5Xr%^PC( z)Wg{?3#g}6!kmTlw?qHv?WCEun2Z6MX$ujL1Mh3`ry{%$xasx7yAj>JVWO_zqrTsx zp4?RR#~#-PXomMI+5pY8{lrNwp$&b|hkX!l3W)|Xd=w!#x9(__f?%2^O4U>Jfq-pY}B{An4gD8 zoKZ-c(<`Jo()0mF11?|;Fc26H^a4hJAL9P}i`6_HxASlw){H~@4s;n#rYH=9$-jy^ z4C_~CVS3%eNoN`e`kg{wJc{+AHqHt4u~!1L3AEJQLqd5!^ZwRiF}^rXBVK>d2QbDV z-V2ZyBM*8A?a2e}8)MDDR~>m8Rqk*xR)#~*!*J+q7*39pL!<-lph8D8Spbv+*sY+W zi;z>wd;RoreIA1H3-}Vf+5pw*RC} zgH>H)U~U190k;C2w!EF3&YYHB`tf#hdhxb?4BQ6H0agQe;Y8%Y?fl)HP5^)TkJEoH z;J_LL{hWBesvL8E5axeB;)a{&=>^Z*Tb9@KhH>*o;Y4eP^(_qY2$YI9B;9m7cuD>| zjL&oLe8+Py!@xMjJjQ<5o6lo@cIWe!Xjtc7t73TFD5PrWA6MCL+{gNdThml8!J2_M z&~wU1T5oA;V<8WTv&P+tkKptq_Ss&)A$^hv*-4;oSC&DCXhC+OZRO!Q6Kj@T+`Z;c zm<^y2etG$g2(1m;YbG2v{&p3q3z{a9dD<=7T6~e|ZoHKGl=e2hS@Mh%llJr;%W6GIkq>j3e}B+M0HzDRdmop;Ku& zeVJCVhO8xP&pL$!gwzYUDWqjcXh_?T$dFDU@gZG8GDEULMup^u6o+e7SO_Hety9%JupPqh!SPqSCrSJ>~i-)n!szT5t&eV={5{h4{p@T!mhTay2PaTHU3u_S8IPB)Ikg&+GjIi-xd2R1*`$XHP+8%EE zLffNl-)Q&c1FNp%TZ5nir-L2$)H{=%f(|>i$3TadwfCXx@0#{IzNgedcPKhMqd%v= zs-ME;b`x}{4?46q!UY{-eRLqSIc-C`&{R5sPN60AR{AOn#_jSpEE+d&>x484X%S)% zX%o^RBswH6q;p7eNS~0AA!9=da3^;@?&NL|bl7RPfesDqP3dl*Z!paY5O4`9XfyxS)rps*MSZUbf^zHH1p9Rx9!fh``R98d#LS^wy%N? zmmgSpU8}0H<(k=e^+W7#RZ;W;C(B=a=2iQvI#4yOD#7Yyd~IBXHl$CCkBtwF4~+Np zHhQr3mv$X-Mge?I|3ChH{={c3dD2UK-iyDBKVK@pwDnTMi*H1$B3x_W}L&$}@fpq}Zo(pp?Oux|Yf`gFrH+fThdhOGzXR)5x zPRm)UVcuvV+WkD-7;j9#?*wB8-X>UTEH~aT-Zb8mDUJ7xQ^rTeCF5J;SLyTjUr1L7 z#XBf8gU+M#=|Z}k{((0O%J6Q%V!U0joULJ-*cP^pZD%|1rom2j4||+F$BwX*>|^#B zyUMQdJlR$APOK&Onh%+en*07<3c~K;zUEHze)B;d%NxghP})L7$Hngyv!&V3Y-5(9%ool6W|SFiwl-Iq&CEozsoB=N*=%lZFxQ&v%~s|Mpju0; zd2LA)Nh9f`KlZOYG6`>cl#vr29=xFuY6~(M z%`tnM4a|CGL(@(g5*@mxZYK3fQ)mkhC0$80_S-}<4th98U<9`#^T}GWkSr#5k(K6Z zatC=6@2$K>-oO{T?$cAW+n^P0owifks@;k8^8xJz?FpIsmVOi7 zVX33H(;J#=^tSpCy`Mf%&(aIAx)$U7umfl36_B6YL0W0|flu8HUi2aGY4?)|Z4YUW z^FT<}B#w38%P`-)6}#*N|7QktV( z0ss9m8LC|-6SZq3U;7SRZ~-{&B1jVowVz0__6w9TUneuQDl!Wv$V#0L{<%8CTi~H& zkse64>aEBwJ(4`AJIF&gYwp&a;42dFy{ztJzut{Jp?4ui^bGR6o=%?A)5u494uAii zyo!DDv_65Hfc)T;o=wi-{C*Mgj7#JiPLAK|mE;?Jrk+aH>&?j9;7!B9t?eaK$#m^E zQUR^@CJE5ilA}1;_R~&aH-3jq*6{szy$zlg9RqiH7fv?!kURBo5{%OWzW+=Tw8J<> zyhKK7pOS3t3v#R8fNaz4)@h4(8rRe^d8WrlZl%?r^qV35xECiNLJ&N z@ReR>eq?@Vo-{u&KQ_e=d==gd>)C+2DMtgVg>IuLA)ZBR38UNSG6Uz*janSLQWZKnw9s(PX-suA*z{I=UV!|4O<6J7EdEg)XJJG>=ZCldvaDrA4%u zPNUOl8CHb_bP-mD#dHZ)hZS@gT|+n0O^h*<*;p+W$m+0q*gxvC2COj)WntJyTC=uz zSE)Vgz#>>D=4P?1E(>CDEPzF`DCS^JyvG#DICSrWaQCDVIY3f;w0ZTYt8 z^gfnG?`P@s0hU1@WSMj~>qQ@8z3IcO4}FC7rF&RE`Y7v9A7=yUUN(sCqffBG^hq{^ z4W;|pFnWLur%$mF^l3JdWzlEYD0+~Mrq8l5^bi|M53_OfIX0d?&$8(eHi5pta_EaJ zm%haE=*w&(eT7ZJema>RW%=|qHif>6@&GzQu~^+iV(rhfSyNvJ!fn zmD2ZE8GWCX(-Uk4y_b3DV{8EZkX6!CY!*(L58|}&7>U##CrR3?IPJcIlg0r&&3YeN zJ3b-#uLZe_10v&9!4I~-Qdje zWnaA~c}7nnd-X)}q~4W4rww@#xBXtiY4wbrOFq=clk@r%auyPwPw^z=3w;{CkvI#| zhHW@OZzqA;dOQK&2npLJoUAwFOWzxC8oHI3+A3nxR^y4<8ay>yNxEo9aP#IxlA^sv zVzj4mTj3y1i_bzTcL*o$XUKT%5;XK&z{#Kq^v5+NOCU8`rq_e+j$pC^r{AS|5ZR#L zOg2Ho^JcvzG$MrH8bFx`zb~C%+?YQoEKQ7ZuHPg&=(__AEzGH@&73OTS%q%x&nM=(1=1jBF zEH)S8?YTMTTyp_C%nq54cy}-JF>{akfVtaz*xYX3iCt}#x!k8@eNKp*`Qn({Ter3qN6ntF$>|_3+@7h79scy5cZ znl;Cb9F^imp>|*BP|?~_)Qsn0x8#4LbpBkcx5utpRD@h+fD%(dZBA!S7fF>0>`oP^ z`z#SAV|b|(4O*0d8oVd+1y$2ji|ix|wu#86Ld4QbugOG0_ zYE=YYEe|EhIgyF7vr#Iiia!PcpC|sRc3{rj;14Hqg15t8*L2O$sK#(gwrRDG z{i?-iZoCm4r^Rat*q;*djq@&ASFIa(rS9ZSNGIOXdXl%bB=WnKOy1E_v{cOl_4(-} zO3Q#WBvb1JzOgsnr|t_iLjAP?+CXg(_-}`ng&l6NHbfh$4a1kxM`$DQjrh?RpDyyQ zHijJ6#%klV@mjVvLCYbNv|M~}Wg>ZBo1{(F^0g`2RINZOgr?_WZJIV+E73~HdaaB+ zrj=_mvC*7jW*A|cqkosQ7``wGRCD3BC4E*y7 z>{YjFE45YHYHba;z65dtJoS3=0d~B}*a1&!o3zc^7Vy~Hz;AEYZij-oJHT(>1-|=k zaDj>7jQI)TKJd(4z`=i|JwTq-9@KVg58+ht2)O8O;G!?#RR6fP7pIITprvO&-Y5cU!I9D{)o8gO5&GGHAmXK1k((QVv9)`E&+u)oLjuQuNLP2&Bi8EA` zI7K<2HQEK4j$4n_<8Y=*&=d8}dKbMbzLL~k@1gh9lc0GoMNh@k@l=wpr|Ic>hMuYS z(tCqznTiuwKd99lfb+{BXyVL*bayb$Geg0@7lBJC1TTc|a$t`Cr4dpLy~CrxFQkJ@ z7^7+WSbdz1dyvq)ldVs{DWOEq73aB0`eblFrTP>d+&|8Cg?f=*tWVRYLq1lD_x;PU z_Vpsa=`-{SoER$gS^8|q%I4^E^?CX&`h2V>A3`F!fXu*C&kB8!z8E(#%klM|W%_b` z1!QU~ac^N2o|^a5*XV2U`X+s|zD3`P)93(w8)VnF>D%?&^&R>h`kne+ z`c6E5or(3MQoje>-Y)%K{XYGE{Q*3wn*(hQ4?(g&OMgV)qd%%Yra!Ll)%WR7=uhH1 zst2H#;A!xK&)^hwP=6NExV>2WuVGIZq92Cr=z0B!{sJ@?yhMiTFYB-1N!~^MDEUKw zO@Cc~Lq7%y;tKsONXg&U-_hUIkL&O0@8ik9aGZ!f&`*;2`iIc>dkTE#Y5gPpjQ+9y ziGEf;r+=!S*FV!g*DvT7p%38;oLoohm!VbvivAVOu%p4FT?PO7wSEoX!TT2a626B# z{|9iKKSF2F&-yR=uljHL@A`H95B*R5FTD!VEzN*3S4jI9vn(H#Qg>A@$pAY%#VP+n{M;yK%d*!??q^ z)40pnY20nxW9%~Sh5m{AjR%YeAtimtc-VNv*ke3uJO-TFa= z)a@i>Z>NmYkVc(>9PSh2tZ~lx)HrW^W_%9W+(pQ!zA!EuUm918uOO}a+PG$X1F6+_ z#`ne##*fBN#?Qtt#;?Y2#_z^;;}7Fc<1eF%5~@+18kAB-O=_dHXaKEE18E&vmj=;# zG?><>4WLn@5p7JH(3@ye+6)rl=ClQE2`w9~sGWw=Fi3>k(6%(3wxbcyywQP1(vCEW zM$=BzL7mh^W1x*Amd4R|nm`jFJ?=ug(r&anG<5W&Ni-SK=(J(-Cwe9YsgeF?1|s(Bo+~Q`YZj7{!XvcKj@$IFG%kR)0oZ-Mt#zV z09IQ_B&yDF{%imn$Of@2Hkiv6*f2JnjetyH6dTRPu(50$8_%+#$t8#7vOG4CO=6Q-KAXa( zvI15JDQq#D#-_6pRtkA+Ih(;M*i2Rl$?R-4hs|a4*ez^6Tfi2wMUd4lfwXoR!BfLBV@RnA;sMaIqq$cXD-o0!edxAX)4KoMWQ|xK>3_Hl4WruJcevUm4Iq(bYMfMVV znZ3eZWk=a->~;1AJI3B*Z?U)8JM3L{oV~~1XD8SPkQjf+PO;PMBX$O|<4>Tk<{bN! zore_pb9RAUWS7_%>@xe3U148AuKYE-#=c?SvhUdU><9KE`-%O`eqq0|-`MZ$I{SnD z$^K$hCNVWrHw}}Tj7w3?T4sP*8(MJcn03t{NKk{#`jDhHG#iDTU zk3%3EwnJM^7$nDSATtg(+nEt&d$WTXX?7G6& z<{&c*8hVD9L(O64aC3w?(i~-uHpiG_p|xkcnQcxmbIe>b&zxvZg4`nCoB~O50pu4& zkV;H5r$e4xDx?)NAh(!lR^n;#kC0rc?)F5^@Qa6VaN`4kuY)(c?r)f z>q27Cfb@WjV*#Waiy-4zLIO!0JjoqTx|5#fQb?+on=8y)&6OnBTt)VgC!j%Sjk%U2 zo9iG^-vH^vE96PCpBx}hk=^7W@*H^{&k1Wo`mqVp>@ARbZ-eA~yLr30!@L9Xk-H!v zxf}BBU69t_2N}u(cm}W=PXQh_AHid)J&^D}1{wceve?`QDZI^m!h8}k`CrWa<^kv} zdKxm7gOH~jf@I}6^LetwJYv3JzDTw}UjDNA3JD_LkZ;M;IP0y2e7qIeOg52?kmzSa zvY%wWN;Z%k=27!CvK`NE?=xQ~+aSYu)qDf;`8UnCgzV>C^SJq*`M!AqlGT%tu%0qc zlV>1nJp(!GC+1m5SwA(;o1dAVlh%-`EGJ9xH2hYwiY$V3^&%wkm(5F%z`qFXN|((q z%`1?#UWNSin)wanuiruA(hug3=1=C&kjMUN{$~CTdF&tNpXOg?m5tamn{G2~)W*2- zL}+FTu+_E&LYiCG76b`zFr>N-pslG9B)d%@+ihxVX1m$e+}6U@5*nOZ+3dDZJc&L- z&O@qn3O7GKh79H$`G|aq=NPALVYb$`Hnz65a9cZDgsr`;gDuk5(H3Qkwso>OY)+fY z7GrbUVr_A@cw2%k(bn14#n#o<&DP!4!`9Q5WJ|WC*ivmCXo^a=W!N%py==X0eQbSg z{cQbh18f7KKPt;M*fzvA)HcjE+&02C(l*LA8ak!M+Q!+&+p=vFY&o`ETb^yAZ4&fM z<=dv%rrHW@g|;GFv2B`dx~;@kYAdsq+h*7*Y%^_@wpq5>wmG)Bws~gKgp#}&dA9PR ze5WVLBhBPEWjd7UQl^`m4rfZLbSDWn(xoW3N|%(N+({z7%c1f~@|q$)hs)uzB^74p zloS`)l8Pr47v)W@om7%vG$}i$ye!X_T=Iq295kM>N;;h{&>*P8BOzwLV3)B1P3UMYTRf zw%+A%*Y;E^-swqJ4NmqpIL_u#4HLC@sw|yun@2WO7M(1Lb~uycWivb;GhNgyFx}r+ z8RT-+POnj_OH|O|^tfrpgzOTTrAt-8=?ToJo`=)pa_gDB%uHX8qX$&QUEa2f)J{)| z@JD!(EmJilQ&!x}EXyy*%?s>RJ#*ATCUM4ywu0F4W*^akW*;B19nP3!+6T>$b@fG6 zwTMxryXE+BrX-pDWETA_S-4|mYPaYQv_8)86it#;N=a5>31)v^Go7AP)s?BLpj6eR zsVa+9S!b6VFb)41@sbjspnH1Q+v>>~5vSswURnBfXkX^16bC4v=APZq)RV$pT*0J89 z;4Q&pW?3?g6OB)bPf#?Aiwn%E(Wx${B3PWtGEQX`r~DITg`5d#bTEdXIoMJi8Q0-* zDl)~XM#Oo{A-=wGI8$Rp*SRn^#9Z-s%{Vs1Pir~4oSrmQ5s#PlDpQZA?vTIJ8uM<5 zmDce|wjpvDvmtz;F^7o3EDMfLFoy~91P=3)Az)Z;eqKplX@03WY*I<~jJ&`R{y>>7 zL3MYU+Z-XHNbfY&8!?JxX$j^?k$vrvHRf@mD>zexjO!I$P@GfHuTQF(6kS^Ca0Vs` ze`Oa$6ZSfSQb`_*BRS6Ma9ABKtHbSc@O4}`f|LAB@`Ykm_ebSRyfwsHR#JjBMAnP(St2}piDmE2INezE_-Qmhy}_WQY4aNFU`N+ zI9xIQ;Q)fF>W`G=%gs~S{cB4-|88o@HBrrrY#*b6Ei6rL%#zs#)9zGm!GORm_)5$l zwVx*`J~PRC+7V1T@=28Gk_6v`DX#KK@|udFam3iN74wm;PB+>9(@l1spr_2rkrtm>JoKY2Vh=!n|ab})qpP6Tg3zNMqjJM@k*(j&V(iv;Zt9G0bMLS%vBE7?vC|DmX-)ZI~QNzGV{#Np$um{A{ zo>ZfBm#8RG#nQ?CV~whcGc9m(^*pdu#G3iOenx+&+Pl&M@&!LNcu;TIF7N3eMb*!d ztmjX$<*TOUs{>BHk0-={j1Qbry*SiMrgp}}*O@x0BrmV1AiF3xKgTQ(9cmW%`U0n& z6k34R$^u#E>+Z|&TiKKx=+0%+k%Swu;P0llmEZMjPD~Wa2d-91l@_Z*A z@6jO9EcOurCmz+?sfrkq%Z9(oB2`w#CC3y_Jc_s>&WUhEi3m zR5jK-&bq~a=fW{wi><>4-+>VPYpl1UlLL$YcH&VvyX9DSxl+v%N#7C+VPaJ)oT}Eb z-cjN$AwwsVw7ER&j}}ptxuf9un|#PMM_=G6q&| zMVv%6JK|Im;?m4=U(aBF66YI4S3TkJn(?gMPiZ;GFmqHjJYLGHOg(9J%l}SkJ~6G7 zjz=nW-YFO7opRsU#(t7$R>*Nz;U_{sg?A-4E5sfUSm_Ux-Iky_J1y3%6j7vin(B)f zMX5}|WvE<8xlFTH@T2@k`HI1K1;@g4< z3Ci{o)EZHS1!nuVS(UfHw#Y#_)Gi{^tF1&hYK#3uSnK(>7U2%e_VZ@8>^zlL?m5yS zPd{?^HD!cAOL(B%T4ZK&fAOWmfk&Mj^KMAHWNX(bwpF$-#o9F~{>I|KNK^FS<*YL-RV@dP}`a%TC#r#@C{Ccm=mQ@AdEn{ z0VumzAcUQ_oNoof5y1BYpEouEf9V*J*1;4bs~=b(mPV1pRxJC;lsN?OrAPz>%Js;X zufrMVr!8NZd?DDq{3U|R_O}WS#kN<-Jmumb?7@~5LU;uEmI)QtPkggddR6)-Z?I(z zk;yBob*vm4>eL_|X|f{%-y8M(tC1zwM46E> zc-&;$#C#E8n<%@LVlm1I@GeELsf8$~w%Ek@GNi105%O9w&b?M-?X{v&!YUV^oS954VSHgh3h%O!LnpvZb9jk4s{3J6#LoebiwRv&zzHge=ip5Vvar5z z*7bA0aM4`+n0aNBVFXBKSlYOhA2yYbpHL2Oe{lX75h$->fUkxD-enDb-g?PA_>z_r zRBb_1x%1^rS@~k-wfei5FJxX{Z|})PET@iT3CpQ#UBJAVa}or27cZ};rC&vfzGX{A z;ER^mDoXS%Sl%4G6p@RS@Z}7-O2&C|Sm{LmgL8gPz$EXm4v?%m{DLgpI-HzLoMDHx zuAgUD0pP;D^x&L3d@1iT8|UEtQT)wv^5TOmth_&xNQ^m{6Nsgi7-`aLA#+g&inSt!Z9Q@3ceD@^F?vg(py_ z2VwQioB(--fG6dVf-GkU?l+ak7S#0kGR1KMzV)mp3LX_}P)>k%V}Z>&X62@mW0PBo zs;0Wzy&)h_f&r^QoS<)-NMIwo#GnZ z%+EF|AvO$u&RqD7D%I#@J|b#~6OT%R+IW*g;A5$RcY+}TPb5cGF9iV!u(az27Hpx8@SS*qJ#7q_=AXg2p{oMp(N)9_QPj2|z! zYdZho(=tGwsf4Y1m&f|a>hx9%^V&-a@D!GM^D=2E&qbcLV3QS#^Y#-zbBSz;yMLf9LK~Uv`ipa*vTcE)Uj}ah~;e`oyk*H@|0diFej)Q8<s>et7~QWR0_`SfsetDVD*B0+?{fIcN5{P=jZb*Zo%wX-Mp`? zZeE1d&D-O1D+W-w8&>B6eQ|K?td>R|L;ZQc&B$$(Sl`Mh`eGnQ zUyL9h%V-U*)+l7?cl$X=8Q;%F3b$k-z13+@jiD3~f_z-1aDu7yMy}`QFJ;zZ=M>DH za0mEe2Km@LnWc}{Q%>)Y@MR+pvXVp%tFwNVID-GPxCIkvb^C}Whl*eat>M+W5mfWJ z>sz@)k)lC07SWOkKjzAc2KkspYjDl3_74p|?`VlHRtKxwSEqV@R?-sAkH6NT#81G| z9Wc>nBleWfVoQn(vb<1l?u6r1UDD_&E$A?;Pg2U{3N5ftR( z9)+{%4OmDOuiDISSe=;^QFs@l3w_P6Z)GceF;Gvwlv&(@r&NV_7d30JFGE#;uRm)G zK2t{ZGn>loTgO$-UcNv$132#~Yz?b3pfa(XM!s?bI8W#cfw0*(w*xrSAVPwCETVE& z>sL{8r(hPX`S?ecSV-Nzkr^m?M_*jUK>FOMgRgc$K2}l$2Tb=?DS&VL!mp0n`-LOO z#{l@UQ(S=GEm;AdTZ%@kB;eAKYHnYj29$e4f_yxJEZ4^<`0`cUf^avi&NBGpNWMY1 z>sy%zUkt@T2zQW=jj#s$=nBcOWb~||K1#|pO0X8zaNl^7;hq{z7o3KO-?%2b;mbww z9LnuuJbd90Ia^ApK6LAP`4AC1$j6LW6Z@JFP~oG#k1Y`q8&>B{gkOCtgCcYHaVf$X zQ0dFf$EwKOs`D!{H!IU3d?dXD=VEaS_C>gZd_0Ub*wUq1GPzr5$hYl4rRweS)?3AQ$>%F>m#VKzK9_L2R6Sj)elBnQy!os8sJnk| z^+e1qA19)|s{Rfy9lYhJ`Z`qp4ppAKT8VV3ee#N>!|hV#;qt4Xv$`hdj#K%^EB|>TL>UOJJUaWPv<%L?9YUIf$(Qqq@ zsmHQz`Ir{|in8+dro%07ZNgOLt6Q6Hd8-rtswJt467n9V!|hQcR^HQuTc!6XN~)Wy zZh5B=;VQjHjVO8N(BV!~?N3wb)eS#)n(B!(ReqXEpC;4Csu>rnW?HP80kPf@FCRxb z-0~tJdPwFM>+*=j0W(lMCQD8qq}<8X-L4wpkn-_sgvnVMGJax0b$3$I4c#djL&T#B ztPoUy6HQBS*6H2=LAz7AN@BqUiJTwdeL&GL)_bHrK$bxllW z&Bc$tG!Bo&0V?=ITq#zEwNNe$@@HVJoA;`gn3d{Um@L$KOUZ4y35g+Rt!OHwtG=wc zguw5RkJ)5ysaEQwTI=SqW}t2($u#QLkaSpX8u3ACDMwwI5z#QrEFoO{g6hbLEUz^? z-11>ROf|6Oqg#htKA?BF6TR`(fR9m2pSr5-RuAyq>K?aS-EeTns0k6Hme^P|k>yQe zl&2<2tePnDCItMwZWTX9O~4qnWV+QvcB^`+d*W`l+5%!#e0ek6;giV`@U21U4 ztKSZ{xDApH zx4dqFa8-YgN-wWtVAd%*q^b1k@`F1~(IHKhpQh5M$@H;`>55g%R;*%vV!cdHf{HKi zSfHn5ezC4Jxs>CAoLJhuTLB(`R(IFf3S#1FgvZx#tF6J8Ut;y}*wpIo7`1iy;@8-U zVrs@utDYYBRjbt(hr#OZ8ui6Z)9T?h>gP_W5niKyZcp`aFvB80-`1$|^SYB{d$1Ks zdP6$T~O3(z=}TZ~!x@)~vje;^^Yii8j2yCd{%5;SMO9jLU)Cr`E*c@)BvupCRLv z=Fj9F0i}5Nu1MJO@+VC$6OoGYRYoG`vWX_o7zWR`Y$BfG@NCN_+C;A0zrFuTqx=+EWFO#qUy}8vd+@R z>n!5&I*WL+&cZ6|EFy|J3!9)zQF&pBC_`9y8Qh}E;8t0NwDB@TJYI%~C(964S%!!x z%HTFhmw3s)IMi~%yHm80RbypsIlT>b(j-fqf`&dDy#3!G41P>@$9?9FS0^F=TvX|M>Z zEiWYTBta9hbEf*8O>UejXj*m&Zpr3NlzG<^&Jr=+WhQtEndc9lz@7f!VD6rphnoN@ zeSPKOk0R!(BPq4^M0{}@&B4oalHgKif#)8Mz{!=kRa+z*npec5D0`vL-e6KWZl~lG z@>>V0tcE@>e`z9GDc&AKTx&Gp<&PF1i2>gR-rx+J+3o*dUvP&vyE-v9xPkBkP3U?_}Eh$N?AETX_mD64x~Z z7m0Uq;>>AzrN~DGtK~^DsfebEm+}lm7sm;h(qCN-#^W1VCgxwQyuxW^mB>+K=#+vF zmrIF)9N?0(OG=6>%BRWj#CY7;gj>*9c_jo0FW&9SCsF$Fo>4w=(q}?(*<@)epO#xx zM`bOBF|L1l^A+wOU#1qP=xaXXM7FV#=XS-W#0N~|<34vn@k|+lb3<84e)c3)?^ph(Gn_F2e7F~8V-zc&I2$f; z@fz{1*X8uS`;uLVxl@Ykz-lBpTuPMW^1hMaebL%`lV6F9Txw-?sq=x!sC@+JnZfO@en7cxNw51K)&(KHqUQYs166HE^q( zAn^53f!n=Q;C5%-$;HJ}vnLeKKtC2vP<4@KnHY7JiBV#$7-w=FJa?OrS5RExkF2DP zF-j&FqYj2KO2!qVq@poO6cM9D3^7Wi5Tis2F);~(J402)cnP-jT1^jUO%Jzkh@naG z)=<%t!XwDnoz`&Cr!w3}N^7|2-I^ZNic1!us_rhUBgL97#aA;GHBN*E`MS~?S@g2- zXzC01cXiE})v6;Mih82wt~3IJEF+ z4P_^~ql?69CHw-UUwm}Fw7C+Z^Szxdj$T7EJ%g2pJUeoa2L9gToQm-BcbL~pC6h-` zOFr@pY4MPUP?3v|z%>h(2R$CANp;eAOAAs%RJBeyUDcg&${Fl0M-oFt_tHi>L0%ck zZ^H-z%iA)rdT;2#iW6Z@eOb6zU7}0zIIv8T6Ma$^D4uciDVkFmn45>&S4FvbB{`K~ zv~U`c{-Q5}ErG(LNlg!#b$zS1C`>IaaeN7}*u6^&_fNF=$0$4QllbCuyEi_!d*gF^ zjOs~|zpSckFZXcN^hm7f5o7fbW!ChlmWL?4W(=7J?Kh-Pp9VAXO3LzcvcZ^viXvj& zYF_m?eFkUe7pTzMez#ar-AQo`28y$3bn>92l-{1f(av}W?=bKrvC+<8&Zb*}66;lC ziK3kia*J`Ry2KhqrFKO-r7bBgT5vlM_Qk6(XSB2Nbc~gMC>htx`4?3*f-gcqyO9R1 zsXCiB&K{M`D_!K}3tVX8eUx)VB8L)HB zxjM8e&VhX%{&jK-^d@Q0MYI>cPvC1j8uToEh~LxDxuZeH&UyTPPQJtM5754Qz0f$XLm%id*x!P-Zw*>KzrgQh=*!okx${@pe}g`H9p3<<_(~a6Vc{EjHt6%# zpffWWzaHrC)}XU;7JlbKi=+lElD8qwc6`lDgGR`w@%s#PLF&*2c?$NA^pEj-*0AB* z(zT3Q_?lgS5e9o3!;RlKqd$HJ82l@D1C5cek2XfbKE_x8`(k`GN`ub9C-Hm0K+ez> z_#A$Z7+3K7mGKo~UZq^WG^6M_XyZ4~W=#=GtK-9hif?@kH| zL*v~plyWb<7xw$<6Zkzq58(G{dIY~O(zo&ZJ_WU*x9$V{o}!=(G}e8J-_Pim`2C80 zjob#-@EgT2DxgIUeFQynUGUqD zb;EBD)&svu45J0w7aW0)Jz1BX^a&)Wm|eVC!uT$39K--kcW$eh}4N<{t|rEBASdOMrvB0!K6`6Wk~^PRFGX(1U+k%&uGkVL&VgHF*UCQ z{)O2kQ_0PRQwyg;4>Df^_#eg-%HlCmBD9d1Bxrxe+Y{a{d3)j8o8X5rOlk*UY(TFt z&$l3;4m9b$X1iwnuhqi;Z+rRg{{eBeCRqQeu3gdxYv0mnXr48pYzmAEnVC{F~(pW~|_7x9gYFUV!`CAmVrB3Gf|d2lsN%XeT6+5_FfZ$fYIIqg$u3BCgT zz&~l%p`kY&x^}anOZF3EvCv&9HBoZCl2U)9)B*W6bUuEJS@JWq7nb^T?6rkvy(oMa z#VNGqaV>di(8AvvI`Oy;Jg?R}u7!UQ*F6WF`fH#`?l|5VUgm8tx6#`d?$RiDTq#=(qnD1pWf5@NG&0Xn+nF00r>P0PGJp$QN6KqtTf zH~|+B1Gs@$AdcM35SJk?*0q}%zCy$50}X(NKqH_r&;)1-Kr13^4zvK;0Br&E8fync z03867$xt3cd7x(_hO$^!pa;+sNC8p-4=@B61`G#A0=YmQPy`eM(}0;kCGuGUb1AS4 zSPN_bHUYaaYVIL7+oogW@DbsonkXG9g>Niq84Fs*!Z#NA#d3Y!_FTk(BZ@}-sb$nek3W&ki3R7V21D*h$1oi_5fTw_`foFh&z_S4V>h5#E^S}|{ z1>i;CCE#UJo5cZ1K!0EWfUlThXQ<8AU=6-sySM73b{}v*@Br{2up4*?co=vD*aJKY zJO(@t>;?7#PXJE>`+)<%Q^3=}Gr&RMS>OUz1v;;zcR)8G{1;T*VKpUVf5Dv5hB7pWl2OtuN2Id0ufLnn1zye?)un1TTECH4R z%Yfy;3gA{?C9n!u4Xgpy0_%YFzy@F=unE`FnljLdfj$g0 zVQ4E$1k!+XU@R~W7!OPYCIORya$q(9x-hhbq5fPkz)%Ns z8c zU8wp6DH|hY3zYQ>Qnx}`zaWR!$RPqbG}dFQu0R#-S=8_xzKV68gd$cbVnpy5VvTWO zjd8){!W!ek8iUo(46Qn6h5?B{XP^g=hHXBb)G;$)X2R?Rzdpb?ARAZ-tOC{mTY+uB z-M~p`CIzQobqPG_l&W9(FR`J$*!l3a;zr={axvTaFGVfS;;U5WNK3TUh1v$At-)wt zFlrr)S_h+^P4$?nzeJn(4i$=6brD(@p?uc~LC%j2Rezu#|K%TI{o#vrv(NS~Emi!Rga4&JZ;kwARN>2h|LGrE zcHN)Df13L5@vD9pe*YhJUjk-DakX9DUEPCs_L&O|t0Rjbj-bQLFoURwfS}-lU=+b1 z5>$d4A{xb=s8K@H@DW^M6w#=0*BE06qM|`mL_mXxh7g0O2#81!3HN``TfN;ocZMO5 z@AL0|y885~Q>XT-x0dd1R^KK&C&de#n}PCG>RTft@lI_^ZAyKX z+JJK-F0s+-q|{g7ewA7)vbbQ2u|UQFmtiUaGpf{9T$_BMM20XPDV+#Hhx*lw`Xe1E zg~}Cs;b$JEjPJ|z&GezM=hQom*?wQ_Bp1xVRGfp9T*YTUH&RO?3E25H3VF^vKxx^Y z`qFp*g~>_e{J(_LaQ*k|4nT8i3#9#4B<_#V2>Wde)q42j=Xq*{aH;>Fprj10h17Ow zt9E<%WjecD5RUA&4RceU8N1X{|BgOOs7B(Cki%YT(l@F11Anu{9}-fRzLXj#Za^dc z<}m4w6h!KTJk+PA9ex`6c_nVd|I~b3CZ&V?Um5sJ8H5HBZy-D?Q3gIoy6+5g<`Zi8 z2ST*F_^%f&m?uzZKRyo25C3dRo0uWtlo$s6L@fWI1tBo`{|#jPJ|gvxznTT&tL7;%Q@faTsm&>q8WA%_ zHJ~LHqM#p73#Z@i2C<4MY=BJnhfn@2Z{)zVuz`PwVN~k;8enAo-nvifwa9Hi1?i1v zjdV?`k6F{1F-01M2ms|yN`zdSz%}i|Hq@iETnqe@dypfbf)cV*N~j5cjQ5?PQLYg) zlq*QXhi`crT6*h12gZ|uFHxF=oG>&-3&XJmCi|*D3i_oCg}oW20-wZY6T>MeEJ|zE zKyZ*Eh&@6_`djpk4IB&sT`^uvJ-GdfI$K7LQlB7C@ZaRr>eMHwSIkqy_dPt*_tf9v zW*5TS7g z)Z?i){O9|44^tJkU#X(hR>bi=$nihK;JpZ`q)5WwH=x~3J%3nasIQ(E_H`o_b=U z*)MZqmtI=PiOL$=mK1vzv1$4dtCtNr%B*1~)fy5<+1v;CC(}>kTf{|bv-~4n!c@o% zQDj3|>E(!JgH%P?GUa=#k%Z3gOuHgl@w64?o;eV-)V|B0hx{5+0s2g&P|G2YS*R!+ zEcqtqS_i)RFm6kCCB>T%zTw9t;VP$(ZU`VLYLp#Fc2`u``NVQvUB+!qEs$lwf~b0ANO_A3SoTTz`=H=Dd?~P_j}4lcn$t} zzcH6ma+#N*P*A!;Mo2(5-v3d%q^6W5Xkm_q!v8g*oGAiiv)qDI$zruTmh||T`A0qV z578A|nLK&+QI(e5G~x42U@%j@!?X?T1xty`s1Aj#k+DfP(@}T2UHzv9;-2Y4)WWm1 zGJ2wTN>5P#Xh3&;!T$n>y@Nk*7o=zu9x3<1C<~PIOwtbqJsZOlNTbXO(lU6%)WO!K zCxTcJY=Dma`VWNOaG3*g+=M!mHsXvTZu$qf&WFw*jl*03Kj6uDhqWgdOBx-5{Cw(v zvHcJIToF+F@*d?!!>QVke_;+~6$tXySjL|WeyQguD*tI#82|4i3R)1bcQ-d<%3`c7 z4{Et!Z1it}zx*^J!%|2Bbd+3ta>z%Cv;;R)i29nylu`XOVJ*@grm zf^6ED9!R7!+U3pg1L%Zf6)C5v6{r(_{{gxUN^CdWaZpwP!6o4$pQV~58@9~DRQ@#e&(zED`Y)~^ zyUh=$v>sT=#TnFf@guZ-#{Q5AUEHe3yD1?w8_;s6@&#d|Az1f-*aRme=$ZsSgo8>}iOH z5*lAl-EZ>WH)B{KwFu>HBl0eh+6XPoIhJNqJGOC#LFN?Xs1U<%@%6<;Lm^Vi4&BO2I_1>{M~8yOJ}2EqC4nEviGv6Uz~Fc zkXh7|vHr?R+?J+>(Dbtn1t?uh6dLgDtZrg0a+*C`V#Mn&Y1~F08Y<^#U5MKd?r`29 zO^vkd?5DsjPP55)Xvl}S81q8%V=Ko^7@f(1w#_dWK|XnpUnKsl7>*ex2VfhV(@BE?y5UkcK`W@%@4`Y0$^S2cN(R{o7N z{dxnf^Ji#t|CahIo&~|L!dlJ-@Ls_T%f{4FK{9CVG4})WQ-tnSn9?^aV>muV4n#8A|CNQVjzewEWU{C!=L*q&E8As{^q(WGpLf4z16Xl<7xF!HP=&g!WbD>i! z*kdo|5#6kiF%3B{6~af38=Thku}fN zOqV2T&8J-Rcon+VL1vZe#MG5~m{d8GYmdJpCuMZYaDOKF2YxIJLs*9AkKo$_{!B@d zDY_Eemyu^cELchTHEQK@?!GRT_!_sVr(q{EET5u%SgR@#vX2l3DgSIQ@$Ll=u9cV? zwM-ZcAs9 zJ{9aS{tIhapabbD=`IKMkQoAIKuH^7QcKJR<8Sgabp2l^P|3CQ?(zi(J>m-IVg>Xy1Y~2F74Z`vLtCS=1(}S@qEe+8peH z#JM_}Iq>U~2ee50g4QuQqLsk(l#V2_Q<64eDGNn{ej;Q9Jkyx%Et^K)wu@3RJ>CYnXXlz()$Dee+${0U6#1__o6;7PgMI5o{gT{t zf6!}7FS!-x-*8DE_z~uxq!)Qz>KbMbP!f@|=%Y}xWgYlKV1qTN&0iuGMJQ|A{J4D# zKK99`7NCFM1{>^bMr!A?Y_?ZrsQY*u2717Olw6x7Hv}_==~0H~uYg}>`}I`j|A@QL z=Wi$Wd@S_@I9q{mrT%ZHOObiYP#7yDa8;_7#IH*bCzL#(sT$*lkG0XS2xjzortS!a zB;|9{)}V=kh-pJtz=*<)AFm_pOO^oYg&Pc`*<~SI7mZTQp0cip`4U}~N(xJgtKy){R3w@ou5&Pc(*)Xky zjdFl?HS<93e<(gmeg^Ga>g7Nx^{%0U_8{P=-z9fYD&9!#YH^FQB7H3CznBKEa@5`ue)%OBwX=Zr|DR0bBDMlb_F%zPSJejfTE+pa z|IjZj&1Tl|Nh)F+m-*#kg=unTMo9Je?VHHUYdjB(xpLq23$xi zL;k)mt*zv9h!i%UFJYQTh$C`SpW^)8#?(Ke8dhH>og*b6TWgdq1sT81t;d&HgRQU^ z?ENp6CypRcX4|9X75s=f3zXND#F5-vka_{KD8JypF6qG@Ql#F28~;GOqc*dB>P4i_ zCRNPQr|f<(7|!fekdhp@!kU49nASdRzC>)Gr5S7I$3W?i8`;z&z8mQU-jaHfLY^U1 zl>xury~qziJquy@ar65nwh0>;$6ZF8?FwffvO3RTBD9hcX0{@0`^PbHhAaOuZH|*| zMivd^2fe6^yb17xKckghwc)b-NQ*10HfC&DE^);nxwHlsFbzzZRgy9!Xh>R0sUZbH ze-ArI`KT9d9P(iu%5VbvKi>fIklC>>P_IU&=7=sH;)j$pt$N6FYCe3}0x7hGsXqi| z2Yb(r3`+yuDKqngwsTwRV#C3j5Z(-BK5fQ0NJ5BbxHR$bokcfFuFb5GtOXe1g-6^^ zzZQgcAlMhY5a)nKnwcX&0RGc5j8V4$1OA@RA{A&4?qFfGuq;SX-(7&gL`rjaJZ9b` zhmoQi!u@Z5Ni9ZQmA+{u@W;IuIt5f-gP~zsHJo?QvrmtP!(qH>yaw!f zP=Z`(ie8Lyx1nt^Z|{LZuKe9EL7$+Vj!xfBEyt4}k5%aB*Rkfw ziqT4NK1X;pW-vBMOJ-s&^#-<|vCWg+EI2s-y_1+CiLqMjK;Q9lYL2)>e-QL)w5N|N zP%^e*?43kN{L!cQ8yr|+*op6tB+*vds;_Vvrx$~k*|e|JZdT;D+Jkt-e2Qq!xXY}c zX;-)>6C-g`)_gC+P{=!1(&OZXZF?MbIifWRsjR1EKM=cb{Z%(&teBL{{|T0mzAitA z5uTZ%l#|{v!F@$7(dN_%Pf|KMBGhkMvnZ~SOhj>v6Fo*0_hm+pA=N;Oi&4Am_$%;7 zs)9?x)+=-iA~Q4@HOJr?_OZx=jbX}5&8I$=39~P#03$CVi@5$hr8C;V5Ko{ahT)I5 zBXK&-FV5qT6C#8(``{tO&pspb*|5cL$^XE%D^kw1_cFSa`K0!OlG0~>1259=ehrQK z0C5-Ah4lCYvXJ;TXs1bsDl_scYaekf>s}YQ zJ`AU{{n9&}MBReEq`x=-vp@sj6IMbqw52^paWnBvuP4w5lp7sV*3w6PoV_w_NR(cy zBNu5@1+d>v&VR-7U;IVAh;WBiWvYd72U!U{XR|0L>)Oa;;9hydA%VEI4D&$Zx{_^6 z5xCL@bb`H(@-Zzec;HSg<2&0SKLyyHqHUKE?02|h6&2z82QKWlf-6(TB+P5L77@)` zh|VujBmaqZ|3j>3O3Rf8Il(+@0@Q=HnJ7&{6OBh96{a73*(C1#H#Rd;3+~o(4P8c4 zW<=$;ks-2l5qe1{@X=q%F17XIUvQWeN*5!gd365!z_Drq43dQvfN`#u>jP|FKpM z$pTZBksn*p1GE9Z47=Mp_lHv^kSN zq*dQOlVkEc^=c+R?HQ#eY#q`p_WeUp1{=~Gz5mjXL{s^Vq#;{V=0aM_mt8aJjf^HS z2Q6XexDd0Cq~$9QE;xcZx2Rldl)ROG1NEG3d?DN#l-^#DKlFTy_WmQd&qw<&db+}t z)w2S$TpzH_G__dfd9dCF-@lBt&}HCmLW{G`pFx#5NC}UuQB0r_(uA7hq^Kv(*dNVN;Oe*%rt8~r4(Kq1Q42r` zgi3la%2d7N!b_W{^KT+4b4N0O9JLfWis1ha+m)`V(!{+D|l zSrXE<%M;#MPd}p3RXRWRgplXRqj_)LUyC*NhwvtSxW+!OJ_uL(ykwAKDEaZjlvC6j zd7H!EHHA3JJp2}*l4A7!$Hn^p&cH>2MZ03BrJ#rokmw;bJJi`Oq+~V<7{(y5V5K6m zmD#lLK+TfFdVdLjEQMmyNm6+|X%g~5QN z86Apb0qVuasL2JW&zgRUZ}`p&G^{o6q)-Q11Md9#OhOYJY?=QFX$hU|y~e5{>oD6K zBjvwcrKLB9Z5x-}pu=QI}Q55ux5?`j9pH~a;#?l7t z*;ydprV*To{9FM4H>CcQdJS3^%UB6q)JT+w($}+!bxUiQp zOevcFlPHa3_!PqwaM8+mi`pZl|7)c4IhdiU7E1I9&uB1mFd=6SV~>(dmqt=-;1=~3 zwuj-t)V_f-59F^+bpp3FYVQElA@Q$~!}p;PiMW+%0plE@r6i{v(kAc->vzB~+{I~+ zd+~r0@3&V71?t6~9qF+#=>&~tCm-uO^vNA5Ok3<;i+qUEHR1{nWqo8Ig_dI-{$ile zf3(M(%+%DokjOy`a4=4px0C}TE4EgBASb)yHc*OIY~RJTNOZ*|*t)g4929rjrcl$EI6&SsC%QS1;|*@cHUi+pZaO2ZjQ!IStV zHsbLeR(j;!6LB>TrNE$939~}XgS6#iXrzS(?cw@pm4dM~u0?z_bqghL4G2F#$HFr; zLVAp9+J)gn4LsE374t3!wg_tjM`jqshbRQ%C@s9c7Lm%v7nd!48Oi(XN4oyFOb|!I z>xVLez8Q!3Cr5BM{de$WVjjldh^CpDv5YLkIt&(kmvlq=#p1@DZS)Cz|DaU1S(^m< z;UIR^S=x17uS~>z3bd!)MVo{%l!M=a58PYiOUwLh1Bdk64OpeKLqFndYCn|F;kRsw@I=Sz17X@|4EJL%upOWHlrc3jF^?#CtAz^O8Ah)~@}CpoNR| z28K!0voXEI1+@cnA}UJQKAYg4)qvqHov$T-6@+uYzL| zsfQSA`0|}OH!4{dP4Jh`t$0mh{#8g_aEv8S#&#+F<=p=2-t3 zu>vZFp%RFR-{K&im?34)VRlJM`;J^f*;|6KjMUeb!I%baENWkSLu2N7 zgWfy{NO0#E0uO$fVCno?{0j6jU1=MIol=_vH<2v#NKa!%UvBz(itl0jK(GJY(9MJ= z2$jJ>l!bfqg%sbVudlK8zh3=~Jk4CQk#b?yt%7g{t5_i(f@8_8)S4*%cnZ@OqbF0k z!xW5s+cPS&j5g%j52>G4*`#)_DTuA_8ZAKv2NpzAarL2(p{(J^ya!s7tl^5dW~?9A zC$zLdY3)#^fZ6jI=Z3z=nK2r*C3{~DnQ-k(Mra7N^!6BwOZZJ(WM+h4dX?FrKq<%c zpGIRU$$hr}8fzI#jLYo4Vw|?e;}_@-OP7QY7j@8AhKW?eaoik*G>Ab%_xQ?PLoyBV zQ6s+ZN49Z5&;n+LJu4ba6RL-*&3rRvG?*8$w9L?zxJ)_Q+Zhe9 znip3I+HX~j+iZPnhJ53X8L^Yyjxh%^znLzPCH-RYPnTs#p$|wU<4Bjwu;imiWS5HK zkFdo2&ZZ@e=c6U?q7oLwR$4dqd8=WH4-#e_&b7*SmkdWVMtRl&tz8?=|GvysFcVV= z*Dh$TH~%Y+c?2zsJui&Qw*vtMW@%!6w1{kuO zqckmc^j4v-0=FO}J_qSwu(5gWR!;)@qy5@OD3%8;497Q3{;&V9s za*BGuQO$aBk9o(iHIQ{1%H+g+l`#0k^~C7cu)tvLJ9gBHM==g{ijA+Z4FXC zU0J;p4%!a38X{|A1Pm#_5OK>My1;3@yX>T`$J{Fh;^6tUI-~uePo}N)Tg}vc z#w&bhYdw2d!J!+%`A+%nj`Xbtjhg;nW&njMi|v&#Hkgq>>6O(992ouA%026m4Tr}B zQJz`0P|_D7R4(?D_X(|Kuk&A67CU1FwS?~n$sU-752MQ`bn@2-3~JizGrAiz)kR+D7rAl=-_geWA@@k|w7lJ-T9K zWcKsLLc-QGcn(r6c+R|!1B0=Ke?d6?`Qf;zUmF@ppGM*zC`u1Y`coj2d9N>J(#_2O zVGcag0b&u%-R^n*M`M=^e|>*qd6wyKmiTAtkX8k6$A~ULVbOs;9{l=8gzMsV=>L`l z@eb~3_B)9(>!%Ea;>aNz>WJ$RnmeQo4$0`}(Ceq%*fHWlWaK=x6ek`L%a2axL+fk zYhq{mqiy(ec?Te(_}C>z!(sij;9G3Q1xw;;-;WAk_5idfNEPe>Sqb;@RubnLh^fu3s!pY4F}x6!KlK_c}uN$7&&d@9<0G21e9773%_6YkY;F~ zfY4<`T;{+3&@A*51njYA#iVU4SC|s@QrvJQ44>lS4o2J;`TAPP-fgKfehSx5P4Q2T zpwHoh?`u$6TKRYim=$jeYndHD3}pN#5;nq$e|*zn5oZo$uOYtv38~jrTk>HrY}ogK z5`Pd^=Oq4J{q?V)Mi?#Kk!nHwF^cpL+96LR4pBV%;0d9SI*C!!VDlYk z^<0Cx4L`$7D}xz!K0*oVy`-f-2J%Y?bH#)&Xcx7hY2>jl0XncjicteXj8ytK!!JIH zY%J$vB7Q!`IB_xzVU%id>;G@)ARgb(0LS|v6T<9y)leRSAC7;&-!V;3|EBCJH7OCB zwQ0B{O08X+{vMj|dQG~&ej3XjqqGBUkkop7cN=VJQ>8}nkd`0s*2))nuoDVzUrCKc zdy}alkcSOg&!!$x?ltbh?CrvwAoi*IEz>!s6$p`w>{X6-8a1E}-vIh3+Zz@X9|PT( zX~Yq>Gs@6E*rXqQ?MmSb?j z5HkeZ(J!*hhg#(eUrXRl!WH(4oRh!Hmq|qEG@@XaUS8zuA#r~pz!3RL3p0Zm)BvPRuukN=f{kC^qq5aG z(1`e(6j~!3eE&{THtFNPs6I=sGnByZ>(O?u4QJ4(GhSmvPx=v+w&)9ZM#2x5;P?iP zFr`gce9Q-7d5*#tQKHAxI3p3+M3l(H4`X9YiBEh!K1#E=i|pLEgc;q$IdM8YEa^{i zxsaBWl65TZD!u+!z`$JMtI~Mmubm4MCkQ6Q zBSdCKpRw)4P-mi?NKxoFO6oS#b70>r+oedTiByswg<|~1NsT^A4nJ?j$Mnz;J0qs-0gW#64Po=ftKmc@=l7{0>F?3ABTG>7 ztp-2cqS%@Go9-`Uk&^6H(i8;!Qosydg`hFVp+sFzbbt z3U4c9Z5ezqrjV473cOJ#U(mo8{)j)`VyIf9JfhvlAvIIpyzT>}f;SGx%WhO;>m5uT zk~lG5aLM%qdCy%?b$IZv2;q|*2 zNmg;68A&%LW$ZHhg&?M48>f9~^m9Iz7JDLk+)bGw9Bf6mJs>-=Y$>ZeaVJ%Sh0k2pu(+L1rhhDWRgF}(WyiEx7U6C^O58LP-j zha6a0j)hCw8nd1#ZE7D%{4W}f{mW&0a9yhx#tky+*wJZwX}ZSDI&ucj_TO_V~gC+BGxE0P5JC z?hK6>al~tOdYxe(krooRjjX3}SAov-7E@3OFmoA3DM&-fdtPWS;$ehDSe6lb!5HZ& zE^dQ&tfUqMwz5WOR&;`I3z<4l5rih&J;d=uOZgA$Ki_kyMD1@MwUy|_ z!j_)u>eqk7E6iO?@^Pt zQR6a!bd;WA4+-wqMYzKL$nXObHk%oH8yLq@$ACXTJ&pUbR!{YmRc#Hh4zrH6-mv;v zZ(7T(*R1#KI%|o2xP6S>(;i?yXZNw6w_mg$v*+21?WgS(_6PO?`$L?s+aEdQ_FtTi z&M%x?=XPhkbGh?{?(Zzs1N9EwP5;}q^xs^^ZKgkQbKUdx=kA5>40ot|n|r@|zWadt zq(2DbypHapUT3eX`@Gl9JJ@~EJH+eZ&hrlQdb%%p)n2vxir35Q z<-Y3GdA;3*-cjDs?(1HEZ=CyvccFK=`?+_8cZ0jtyV1MJ-R0flP4|>H1E=Ht3a9q& z^?u{I-W;4c-dyiNubKCtrA$(yIPIuK_ke1rC2(DH^td;E9t7)TDl*&XwtavrNrP^EZ(dIWTx ziZe(3K#c?aBQ+lMIqE#sTTQ@OseX(zM@>{0fu5u;fjL=Cf#hX4bMSSEt3Y3)u7!D> zngbsnz`38Ai?gqK5a+q-A)L1Qt$ITBR8Jz0_gBxV7h(PhXLmIZr>*|1UPX8ps<)wY z3C@$%JL*HwtJU9Cxmt^}wfcwJfG_KOslGz&w&2WBUt4)9*UGmF)P7c>Rj4{yMHWV1 zRyj|LkJF2Wc|qckviELZ%qPyiFKXIv97mngzq<5_bJ!9 z-z&@~pYmqpE}TJL`9`VQt!yP0zB{p>R3*{$sM z>Ii#(yBp|(?SqxJE9^r+_pobJ7rU2Tr`nm>>;X!{R4ZHI@11;JwX-Q zKem6YlJ-RVQqWWED^z=Xsy$T=vwvmJhPFHHJHffjehT!{_S4YtjQxxnX+LW}r=&K% z0P{urMVRyKd64+}VEv~vvT{>}i<1D(NYkaL`KqN;XIa!vy0WM>%YQ=Bs(Gs+nS&S>XM&_8sph5qZD z>)_Y*&h_d_=LY8n^;2h>GY$NoI@48+^K<7Hsw>)**)Z>P?o%zD`<+Lj;djncs@8eh zc>(kvo%!l0=OyPQb-S~`c|%n=i=8Ei#XHV=Rp@--d;xlcwpEFCw4<779O?}1>O9q4 z=j%e%Mi=QKm8Xk!E6}ZVYvt%Rx*h2Dx*YTY`XF^6+Ock+57rf+57CD}Pj_9dTIw2& zbkKcuUsbD**8SBGw0c9qIYAGD&QtU#(4+NPs+{uq`T~7{YOOES7sB=;jWVw<(U-t>vc44jDf)77exiS(Y<-2k z0`!&oO4V0S)l*fOzDi%E4%S!eYgD-2SSb-lhGk~ioZpl6z%2KuM^r=V}t zH-f%N->eSPKhr-0eT%*o^mIL4?XQ2Xe-529^bF|vg}x28x9i(gj=n?Rp-$4j)V~CO zrk)A@|LFe#XO^C&j@Q4^zk-jm^=#PQsqaKc?$URuf%p!2la!>(+}x~5R%{O-y(Jo>qk^a{iuEv^1suM zLH=?5I3%CYPpEVBlln=7IA77PLUN)03;2ulV#Mf8{U$hX>9-KhxAohg zm*^!3+dFzGT)nH`Rrz|EUIzYhy&SGq=oN7Fo_-H$`@Vi3`Sn-*SJLdLT{QX$}4Y6LM*Qi|mcl~$ubG=r70?q%_>*4k@{TcY5>(8O>3;l)a zp*QF+kvkjpM(Ek3H^Z;5^jDDFqVer0y;W~jb^06q4RT?d-Uj)9>Fv<)t^O9V-l2Cu z^S|{j$fsOO<)F`Yl+#GeN+stjIj&t)|%wf8PZlOxLMQ#!J&D|ED zTf40x+170fvz^;cb#U9e?IFLvTds=T1Kb0^@8DuboZHnsNFCxH>{h_UsoJ^S-R_X@ z;r4(`Pq(LX-72>VbT7A;>hIRNwQyDE)`8R8?G0^*yN5$_AGZ%|k8qEG?UC-02+2`y zU(iRpN2|l#er`X|{oVeM9Ow>&+hg5h;maU*km}+Nc8`OeVK*L$?S>TU#&xT*)-Sfbi;7){|i`OKm;9(SLB?UU}4 zu>FJk2XJ0+Ur-5Pta&j1?EYExcIUhE)eqg5+?Ui(+y(9e^)vTn_hogX`-=ODn&Q6d zzN#*97rG18c=t8;HFbmgy8F61-TjOE7r0vFE>gdA-*DehSGbGa#p)LKP4`W8llzwY zmb%P++kIP2c9*zI;PxH&9W~8e>Mn)bcinf@hQyUF#j-bx;R*9leg~XduGQ>O}8A??82m*Tw6q`T-qw1Lt7xU~mrc z4pCFR?p}8_#_QqrP{V;24})3h0a1B9y`JhS!H#N#U`Lq1j?jFRcNEONUSD;qceHmj zboTcKAUp%Tf$9YBSnpUh&Ku+n0zKFp4Ei|lI5o;U-a8(yhIm6j5A}wEKEXRt4fam* zPEw=2lf9FnXP7q(^eNscpoe?Ip>u>cLS5vY>Yb`4c|Y)epsw*w^G;LOd8d0LVS9#m zhC16D<&9E5@^jL2!=(D|XYAo>Xg{sWE$h$~& z@GkZ)20h7}q>lA2@h(yOdy~D%>U{4~?^5umcvIA+-eulpYM^(yceyGBO1?tP@UHZ( zR71U~-c)s)ca;Zh>0Rwz4Gq_L*Qj>hwcfR$uk)@`eZA|w>(wv38@wAJf1`IJ_&0es zsXKtMZ$U2H>P<(efU{u&XT!YLyH_QEwSNPhz}f07;Ov)`?Y-i?qR#VP_0Xbv3%!Nv zdha#wb@2b<{YBmEE%FwriQXIDVx;HW-rLB{CEh#g2i{U|DP-RD-bG1R<}Cxg+*=NM zg||XY5Y((NA|ls7Qz1UD5N!)A!Hy^E%K+MOeDyu&bvjxO_GEBk8n)vuY;N#0w zGwX6-;+Dk3<-o*qR36cA0%-URq=#VKF2uM;S#Mh_zW9s+cmu)Evc!4Zt>663ZZ#%)84n;^z*Zx6M{0j)+D zw_Ob5cC@duXF-Et+&o}h^wYq#e}ZN~u{Kd`0x0%vXn4n74pXpfM`GDhdlj&3dwaFL zS{+41+a8D(YvIJQ4zX+wuE@ZwF7ycy`QG(?BD zK-;arh!!zoYhpxAjMy!P5qlCNmJ%ac#E8v-5obYg*f6amao(Eqr z-(kdj7BJtRfPUry^I61vnwYOSF<-Y3^Surq{{rOKjmWP#k>6oJeoNp=&|Y(*y#k`W z3ZgwtwAU4AFF<$|M0mMGc$x^$BDT}ScKO717ZckR#IW5YV!M1|y90^sS`yo7U_1N~ zQ*Q>sD<#5f286c-`SLYzo<*EjMVxmTao&N%dHKY7xx{$|F`Uwqwj)xsV@UBQM2aqvVml(mE<}nhkz#^K@eubIcL01D2#jbG zBOVgNi0#}V?hxo7>Yjk`3tmhRFAgPMOaLzqM@U8hHM;Jp?x~2&58Ts0pYEOx`H}8O z&}X=#k-}#JN7}@Z?T910xZ~U(!FIfRF8JpGQ+Dw&<@xUUaC-rerAuVlj>z%~cd|Pf z`Y&}ag?xlEk0;7>-JiHWK`LG0UI7_FoHh|>8}};rD$s&E+Yxtmaj$i+hkiky33r-1 z4e|zq-t0~X|K}kRO}M{se}OOv9_>OrY7>vT#G{wEGu@f6{U7&#pnsP8E4ZBvglfCL zcJBi%ICU~+`!JSs9|Bsv)cvjdThI@?55vbtfLvYockW}5f82c>>GK4TtL;ARJ`Wke zug4R=<`Tc=62Dr+uf2$0&m?}mfcW*t#IGZXUw=US+Kc%0JmS|QiC;$%zg|Q9`a|N^ z3yEJR62G27{CXPkYcJy03B<2Q62Fcnem#izwGZ*@LBy}ah+l^jzm6k*9Z&p<`Ap!~ zYU0;g;@3Lj*B->LwZyM=#II)&zmD||^$u0NiC<45e(mj5c~wA!HC~N6+^h9!VG4dN zBYrI-e!YnJwFmL*LBy{$#IJ*iUu%e82NS=R5x@2&ek~(@?MwVxN&MP_`1NSw*B->L zM-#suMErUR@#_fU*B=qTo^r*UoGI*>r`hV*QrFV z1BhHZ6S-bZk-7Sn)uZse$_;-Cla~V5V=}1-;8(MwNk)}&2Z%bOIr5p&UR%h z?XB^!66JmA?NWI;WjU3qea`Tlv+?ajTU`j*&Tu6&(4EyG#JI;|F8LVFBAM_-&1) zJ;o}>-0iVe56ssN1xm$yt<}pq#hPQ)$;_+uGt98QY25~txg2w_?^&N>uJto(J601g zdy3~>wzmFi=U`>wV?m8}G4SF9%%e`Uudt5-E_~WP0kfMg*f(HabEW+=puN@h!}iBc zj{Q5#N_Mc{1Cpz@O)7;ba)99tf>Sxd!U8w(r-erpZi}V@# z4fGQ?=*8$8Zq!TMpSic{rRWLn(96;8->=_C%RU$DJ80AOI<(`@>rc^A{~2fmZ$mf7 zYT*CHWnm?BpI--N*SWj+?Is+SRe=$ZnDg{ECD+Qi-xj&{+aPyP+vVDCS3(=x$ZTHF zyrAUTgd9bs9pY!AZK6e@ZD|7cwz(stGtnV;WTHc&YodF^GgP7~Q6*HOccOQ0N&1-> zms^q;=byt8eG|jNb6`jXydjAp&69Fw*qoL)6K9|$xL=V&61RYJ8>m@0@5!B$`*7|PxzFXk=$i|2U(fwG z_mkY6=FDH8yEA`%{>I$5z+0BPl6IfNZgcLo+-=E`xjRd@=Gl48k}npFmou-RU}SPz zK_@x$PNG?vm&`jbuNLQlr8Dzd=auJG;<+NPHhDFu+Pow4`sW>Ir1MUNi}uDf-Hyte zoHsV_+`KDrzW~={Q1kMh%3Ff-T3$EiO~>;cc`Nc})8^jN9eH!*%6k;1NInm{6|j3H zZxNn^UXiydZ%y7h*l*97%>f1FkWnv+wyxSU(By67@yxezi6A^)QM>+{ddA4h6R z{!~2Om49>ojQpATcO_rXpP&DJ{zJU({l?nU{Jvc1;b5zlRJ^K67w<0-|+=k7fdXeRB$=&(+X}WxUJx=f)@)`7Tm*Y7Vj$y z=HO{g!NUbl!0tIpECAzm}o-Ax#SPtHSg%z}`1@Fkh{y2{-JgInM;i-kAil-G%;yJDO^5UzDrxh+K9E)oO z&LxF6;+$MK9p~iI`wGu3ynxSF6izRmQ+O@t>4kR`&Mur=crUKGg-;beiYqvuFPw*S zN#QHhvx2US9H_g7hpIJ1pXh_j%MZ=2Dgn4GsI9^kVrW8%UHKphxF-r;6}>NaP;2EXT3@uWXh+djN+|HX;=JM#oMpxBi#rvMDIQcj-aotH z-mUo1;u`S!6!)Xu3E&Mc9*J`dsEL_5Rs00bS;f!coK<{F@ojv%2TyZ~9|rwg@r%U^ zieJaMviPmyk6|vuwGz~3oS)GAT+Ak?N`Xn2k^@U>ODak#VIEg|I<;gJ zo~|f4x8#D7$+#~mxmJ3)l2ts{m9{O};GfbnE-LL?3WQKHw`4ocU8TC@#*!^1uarzL zxdZ&(B_cn&bYRK7kQ1p#aXwYDtK|8Tc{pEzeczHr@MB5I3g};h)As?caYC2q70oS5 zb!h^;7Nu>8M+W-+7yCr?bKOga;2ehDs5|>W>Gk^J6nn7|yS1gKB?l!hFWp*tX6d-n z_mdNo&n2HLoltsF>0_l+N>?YxBuAD`O-?F(q4av3^Gk0oospbadROU8T=ym0mp)Yb ze(7VlKLbgTnqRuGbZO~goJ*l?OzCR)u)cI7^zXpuxx8ezWL~l)S(a>{>;(GIWKFV9 zvL8ZlLUK-Wc=9%wbCORaM?%|pP`BWmn4FZnJb86;T5=nnUWW`?xa2*Qd>FFNC11q3 z05)$Wm%;AiL_1I@J<=?!(BhFRvkMh;eZvF8eNlh#z4 z*AO9264UM`lGZnr9EE)jmNm-3%%4>wlImWXa~-TXSgxUIJ|m{}5zU!m+LP(yQPlGg z^*rQ!3z>%~^N{GYCzJjy%>lF>;2^Zto#a&0ycK&eEbCUE7FPpF52JY+%^8g83~_6Z zqQ70}?+p4oLp0df(*e^Ve**av7_a+9gL;_e7KxYIBD8%LHQ!FNAI+N>lAC;5LSlTs zk(^O94|7gMXb*FSz)U%9U@mrgkTXn7`xTlWkh4E&!RCiKZArfa^L@KDO_8Z1U1u9w zZ0l^~sq>O8bRX<~fk~Zxgb!NW_Mv>AZi#ydLZe6jKx6eImS07O$IU+1102kd-8uwbGn#n6JxQ7;oL;N zOKmRwa+yaiHMqX5#Qh2Tc9@Sibz%x_jbQGKU>Y7w$>nw{@R!?|r-!+K=1Q35j7vFV zT~5hz`dH4mlrt9P^s$uuQlFMoDy95rmY>lQHv3?v)i$QpXr|R@x*APwqp58)!#0|F z){3pONN77jx9`#I5p;V5Lv;k*9znN9Fh>39?-Ak(xhX4?vm_twHo{l4nHs-jYW$L^ zv6=Fl>EmX`cQZpZg8D~L{|MS9$xr&U#4#yu?T%84V2UqALR-f=SJJ!~oaWf~Wm#*S z;pAK zkBjJI4@Z3LK_7cKvX0t=+IrC69=7<~!?})p@v8^5l~GR_>B&N?yF~^jG^o2-%kCBp zprHXKWkg=BBfXAphx(@2-a}XS(Dq3&t$S&XF|lLV#`qlQv8x!16k{=-u^7)-jCXE^ z+wlzlc)A_WSd6FJvwTz1c07F_FCn*0jLxC$V7eMi+ri|tBBvEKw{nncRx8@JGPVp` z4mmjtLk{^{eN%F3EBRZgr-F0^T~$z~!tfd93Wm0V+PaY6#n6=RLiv#_KO-e1c2}mu z*Gz|zOox%wKay@oQs+qO9O=82ax#)SSCF$pByF2|-lm=^>ZzihD*9VRZB^7(MQv3q z6;;$wMQy`KkF%xhjbqsxXa8MjDVb`GXj5xgPS%Jv&{9Hf7gw;Aa?7ez zku7~(k+_BVZ|E6LJ;UkaaJn5%ZNsT~ICT!Eo`c9g$k3EOi1OF4mpnmCHP<-}Iu9V{ zGBIss{~4U4$r(rUNSb45`(yhhxO#+~ztg;p{K2Hh(7c89SeKu{0TdW_G z^B~OuH2c#e!m%{zABc&T5T-SO=4_fXX#Px0+a}%DZUdRa+2S3}R`GPwr?d4tj`Rg= zIlIt&j%Kl#Xm5cE)Sc{G?`8`3W&d&pxK#bCz@MC2Ck4h)*Iyfie`mx3etF& z^x-rI`KIvS@Ixrr;B?XhXdX|Q<4HHC*@Znt7oSG|Vs$3ni6MNB^e1#RfaXY=Kcv~5 z=3z8DVV{M>RD3*HIJQL%!)SX1)8`0k8%)k%rrTgY4IvM#BIZ*xNBgG4Wi9C=X!iC^ z;SZ)?gQ;@_=@C9Hv3QE~HAHk*A_w=w8i!dKku{P&v1eDQBi_QZwB+)Jn58_Wj$Rvb zEW_!C8iQ4pD(qZY=}g7SiRT^U9fVbt3atYPC_|3J#(s6Vb0*3=v4;#;U8aj&iyBHC9z-VV}^`pk%N8!(3H)imNJbaaH9V zuBxoSs!A9253HvgW|hglc=>4Wu@-&xbKd;LvoCRR?q zu%5LxVfAFO?P2v~4OdT+Ts>*a)sud*tHwTA_S0Zj`LkF(na0(VN3nX+!G4_UCR=3J zw&TctZ6_Z)U`9Gc*pdAcr?Yb<)<}-Q-j`os=XRDg6s?@=w2d{CSHd-vR#-#9E@@{a z)=9cLA7X!Wh4TqlQaG8@!Ej8TLB3dK%YLZp3;@x&9gU zC3MvHx}Dtv^?lfv+7&A%-Q0t*VsePv9cv}nd7ytQdk*v?Tu*tF>nXqEddg#1PZ@yK zlVjcEv2G&kDSzO4%8RlCK+or@%1c~TS%6iQ@mNWjfVGvkuzo*9FOwB|{hqABV-4k} zSXKFet14?`HC_J$>*%wws&W_BRz8*0alHlW;=k8hxwf)X);RSpSv%Aze)auSl+aNq zsbf)M&s9#Z$!V9a`8<5K)CG{6jO#{RSKzwVr>En72d>$;?#1=0^(4&6*8ZrE74{1> zrH)rv&%#_r^C+wo3TGT7cUag%Zrx#>NxC!WU+@i%6RrJGNqO}4dgnETs7&$tO7$EF&E=v+D05U|9j=mZ&PBGBSkLN>w$jK| zV#Um-_LyGAvPbS41q{7sV$z|isP1juceC52#S1K1l z|DI4!?Xio-{b*1hCWk~U*Ept?^YLr@$#WovnO(0h{ zZeLK9>*{NXcrj{&$W`vbwWWy^-z=t!km{+8Z#|28w&>Yb=)I&8d#q*8UHr!(MeI75 z_%@B|*&Qt*QN*55J=u;o)dkbMDhyTQRPR8pc5>|%hHjkd*|%wO|2-}HCInf-cWsy& zq#_&m4eXYQT8sE_wU zH8qUK^&vI0iPX(mX|*( zvga=LK}eBa^G(c~PSu8ZsHs%Xg@GSUbs^Z%9F0?g6V1`{{hq6vL@jNiT;uxoM$6u~ zZ9G(K!_)|=UI9z}r&P6F-K^(&QPUu`F|^$pP;xg%m8$VuguS79?)d&_iI8O!qq@FT z98m+IEC^z5sH&2xva0dI%bZwJNoeba1YwS-s!8>Ek*%Tj zC`Nwx>dvg1CYt{fw?RtM{cS<`Y9|L&)vS=ZC!nM>nIjt|B`4E1D-Wf~992*36Y9Av zS|T2)s<*0^)v!iay;!xN>UD7u<3+YY5us$*M!Xk{Z*`B=JyS<|@2IMkd#Yv6UF?gH zBE3Ei)A^GyU4>I(uF zyP+nBa(7f;QGIRojn&huIjR(XuW8k@jeYgp>PMSI-P=UD#`W!umc4P?c&M`Eblp~y zD#-ok_m--GuIfdirFtH&SA>f3mVmb+AZ`Sz=^nLnWTo z+f8iaRNah#*Em(PcK32MMaYS9B3Hc&lopD-L`cy#k|TAR*ve7Uwx+{>iE6PgG)JX4 zqH4OD5Th4Dx$JdlubN(cdi86Js_9^SIsxGW=*{EK_>&s>3jZBy8H6Fbh5$*1&USq^< zx?IHTHPLA9b$PF=%_`_#Qj_*r%bvT~2O&j@Ofxax1M0TWMN_F;_SnU&&_(0aJ%L>9 z^R)$rHv8}be&)Tlb#1xfWuqeL+EXKXUr@EpL`$t5=&C)iwxYJOwl;VU?q;WAueXpN z%K~0g?zKk-QbN`CuRRVL<^(*s_j)*Z>h(lOZR_=%q0N4{+OdI6L*WbT;YXt>)QfYk zvG|5Dk2DCC8Q)$j5z>#vCZgoY9KAjP|MTFfsrO!+1IZY*t=Gbg z!fKOZBS%z*%rkinrc0Ej*{DcEL`8X7ds4*BPDS~Vb+>D@pC;Tuwh8D0=Ent2?u9T-}6Bowd&goVv9_)o#GG zCFEUHp9(mQ-Rq_Vwh>i3FW|}D^tD16e@S(yT_kt#xRzSGq;^H^D#O{GS-U2Xs9h&i zEaqVv1QO<+8QHM7NQ0T7n}q=t+#|OEC;cA9H!4k$M6ezot-3{a2v@YC)5sb|-Nw4DK|C6xpl^Pl zy)KMLn1Qhw^_aieLVz^~qo>ZNvV{P9y93X%Q_*`JB_i0ntatm+fg$j{gVgDGudELq z@m&Kk)hfOpB6(?9my$DI-X5_o6-k(5VV*@-7gMH&=C5dbzg-2}`^lM^Itlbl;i$Q^ zy^`h+sbP(EANbE$H$Y|{-v$)qjISXWlae!iQ+$;2f$t!Y^N@tcK9{Z@5hUs*OobwKWK~)eO(A!m<4?CWwsvLz>cl;|tG}Kf%I#Yt|X$oI%bE zG1Xg4u?gh-Ds={AeuX!F@J$5TN_&bo-WlJyjPH{Ya{FxRKbx*@_e}}y?UX;tnB*U6 z^#T7VhVv$JZlc@MY5sO!7NX!)ls8HYRO%_$vqT=Iq>Q|<1e2UaRw?Mde3LVelCP3;I5{Ke%fXaiPIDFN?h0k^Iq=JeIDGB?ov4A)znUi9)L-R!4A7CBGjz z3#@W*&NLjl?Z@!&5TmE;3I!<#W z>+eX`-!&qm){x(W<{WD@IQLlnAU~P0s1&}$-a>`ruR z>TeM>j9`pL;0;;J>PEl1QSzs@r1J^nTqmXyq=I@!^BeMa(L9>1+0mrOu%#MD`bhGR z6xyC7rh0^?^vjktn=SU=g?5-1w#kL@!m*{#x2y-*-kP_;A0(eSqMnzq;k)hP_Hi-Q za)$OWy81cI{mB`|7VH#qW{8P5mBnmBj^I+uK9BTl($}+ve2w&P#8mH6PhX*}+sMze z@MgPJOxtxd7ur+7G2b*fi;~CFWLn{!NcpCKDJ}Su33M)HTi1@Q&{j%LU`@Z1HNA%Q z_fGPEMYCK?dw$D+ep&v zcPx*RvuIKVUuBi|JVyw}X6{&5kj9_k!Dowse^?291kIOd3PiBf3*r{vBM=Qkg<~_< z?B7!68ZjOAq1Kh8ACMMIw_ys%``NadXuSf{OZ)0AKF^P@-nu!S>cB7HcH$Rs@s$Pq ztL#8kf&ZpmjDMtEE&o=)`_zN*Huclidh1)fF@2}~NBrCBB)skX1>RTghWC2=79rVvWV$DrVsA_UEj{)&_i;p$vbN=#RX<1MeK;AIB&M{HhcF zlcm6&3N3~^RK7N#5hieNhM5rG9r@~njW_W9XZu@xp}|HN{Ab;r_99y(^1z3{Le6WH zDo3d?y7?UoeGVjSa$4Ze9hSv!Z5d3ePoVG8E{cS&P0gjYtLaDNXL*L7{63ZQNBXIh z#2|q%2yQXD#HD<9K|(%|wi41e_#FCWzR={H=xZ^2%OcWp7KOSPOY?Og>kD7w5ED!1 zUHQ_7BlyHt())-G%ie;%Ar>cmjR!T#dC2D$DU%lFD?IG?o#oUcy<5y3zJ&vK_E#C& z_{|eXzNHhBMK2(^&?O_C_+1M75qxt(WPP8VTjYyI&ZiPq_fBb1T(*(;jsx-te{(qw zrk8_X0_NB`4qFFH;Wt%$mbOA#Hc8?7mnp$6zVMKTs|b>VTa6bM{BObwExB)iLM5oC8(ooP%->RtM%BlG8&S z#Fnf=zLsnq_Y&_y#||r3!}^^vs9a4uX3&UoHDl=U{ma$tQ-<{`SBvrgSn~}D3+3AZ zTJaSA2MKgordpxqw86iW+T#zR<>~WG>r@-3h_Q$Dv*7dGmVacirBMzj<+swJDh z%^P&lTkUuB$N}YQ!ZAk=FIV>*GYsaE}dCA#o?b&Bt@*}nW?6c3mNNuFKW8B2^&as+}yWqm}t%C8B&N|ylf^nAB ze&Pj}o^N%Uc;VR-t!^|ArCCF>56ylw2hlu%=5U%LX^x>ep5{cFlW1NJ#gnY7FBJXL zE}S&(0_&EGguY|a*%K#QvoAqtt@|!H?}G8xLzkR?_C?lXmt1n_Vb(L3Tsrm=>xD}$ zz338a{$!CyJB)fQwN)<8Q{X=l@~3edpV=)aLF*TMW2ZK6L`^A6<};hW(tLXhuSIK|eZupk z7V}#yYq7m$Ny}QC16xjMIlSdqnA2Lmf^$__hqC@<EM5 zA8Y?u$9Wx>!Z-1;nX>gryBq#P_MN@c{y~7XC%%;J34`<2gCb0Zs?}gX|Ee zyVJut)Hw`aU^>>3f8`$U3~`1!C*YqJC&}M(oiWZ?&RFMcXPomRXS{QcbFOoqGr{?> zGtoKUxxl&5xyZTLndDsJOm;4HrZ|`3f7?HCuD~~(raD(SS3B1@*WznV_@^1Z=X9fU zlXJ84Gv^lPR%bf?YBmF3c)AT=c)G)x>HLo~%lVZv+n$QQn!U8!aCgF2q7HPrI9;8C zoNms+Y4PakRN>1~y_{O7>zO?(}hvaE`>6ruyPbQ~mH=Df}7OkLzIk2Y5LC&~d8s z1Lrj7bbNv842;R}Kj3{3XNmE&Cdc`HXO8oLGuL_0dC2*#^RV-X^Cqh<6r0RI7^*(on_8)XNB{g^S<*}XQlIjv < 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 VGt1clU7|DjWg`+aAKPR5{~Ih^NSy!x literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/assets/locker.png b/mobile/apps/locker/assets/locker.png new file mode 100644 index 0000000000000000000000000000000000000000..d8174de79e25b9ca052ea63f64c3e7aea51ff10d GIT binary patch literal 109405 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⪻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�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 + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + App + CFBundleIdentifier + io.flutter.flutter.app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + App + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + MinimumOSVersion + 12.0 + + diff --git a/mobile/apps/locker/ios/Flutter/Debug.xcconfig b/mobile/apps/locker/ios/Flutter/Debug.xcconfig new file mode 100644 index 0000000000..ec97fc6f30 --- /dev/null +++ b/mobile/apps/locker/ios/Flutter/Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "Generated.xcconfig" diff --git a/mobile/apps/locker/ios/Flutter/Release.xcconfig b/mobile/apps/locker/ios/Flutter/Release.xcconfig new file mode 100644 index 0000000000..c4855bfe20 --- /dev/null +++ b/mobile/apps/locker/ios/Flutter/Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "Generated.xcconfig" diff --git a/mobile/apps/locker/ios/Podfile b/mobile/apps/locker/ios/Podfile new file mode 100644 index 0000000000..7e99f94391 --- /dev/null +++ b/mobile/apps/locker/ios/Podfile @@ -0,0 +1,59 @@ +# Uncomment this line to define a global platform for your project +platform :ios, '13.0' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_ios_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end + target 'Share Extension' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_ios_build_settings(target) + target.build_configurations.each do |config| + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0' + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + '$(inherited)', + + ## dart: PermissionGroup.camera + 'PERMISSION_CAMERA=1', + + ## dart: PermissionGroup.photos + 'PERMISSION_PHOTOS=1', + ] + end + end +end diff --git a/mobile/apps/locker/ios/Podfile.lock b/mobile/apps/locker/ios/Podfile.lock new file mode 100644 index 0000000000..a6199b577f --- /dev/null +++ b/mobile/apps/locker/ios/Podfile.lock @@ -0,0 +1,225 @@ +PODS: + - app_links (0.0.2): + - Flutter + - cupertino_http (0.0.1): + - Flutter + - FlutterMacOS + - device_info_plus (0.0.1): + - Flutter + - DKImagePickerController/Core (4.3.9): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.9) + - DKImagePickerController/PhotoGallery (4.3.9): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.9) + - DKPhotoGallery (0.0.19): + - DKPhotoGallery/Core (= 0.0.19) + - DKPhotoGallery/Model (= 0.0.19) + - DKPhotoGallery/Preview (= 0.0.19) + - DKPhotoGallery/Resource (= 0.0.19) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.19): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.19): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.19): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery + - Flutter + - file_saver (0.0.1): + - Flutter + - Flutter (1.0.0) + - flutter_email_sender (0.0.1): + - Flutter + - flutter_inappwebview_ios (0.0.1): + - Flutter + - flutter_inappwebview_ios/Core (= 0.0.1) + - OrderedSet (~> 6.0.3) + - flutter_inappwebview_ios/Core (0.0.1): + - Flutter + - OrderedSet (~> 6.0.3) + - flutter_local_authentication (1.2.0): + - Flutter + - flutter_secure_storage (6.0.0): + - Flutter + - fluttertoast (0.0.2): + - Flutter + - listen_sharing_intent (1.9.2): + - Flutter + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS + - objective_c (0.0.1): + - Flutter + - open_file_ios (0.0.1): + - Flutter + - OrderedSet (6.0.3) + - package_info_plus (0.4.5): + - Flutter + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - privacy_screen (0.0.1): + - Flutter + - SDWebImage (5.21.0): + - SDWebImage/Core (= 5.21.0) + - SDWebImage/Core (5.21.0) + - Sentry/HybridSDK (8.46.0) + - sentry_flutter (8.14.2): + - Flutter + - FlutterMacOS + - Sentry/HybridSDK (= 8.46.0) + - share_plus (0.0.1): + - Flutter + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sodium_libs (2.2.1): + - Flutter + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - SwiftyGif (5.4.5) + - ua_client_hints (1.4.1): + - Flutter + - url_launcher_ios (0.0.1): + - Flutter + +DEPENDENCIES: + - app_links (from `.symlinks/plugins/app_links/ios`) + - cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`) + - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) + - file_saver (from `.symlinks/plugins/file_saver/ios`) + - Flutter (from `Flutter`) + - flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`) + - flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`) + - flutter_local_authentication (from `.symlinks/plugins/flutter_local_authentication/ios`) + - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) + - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) + - listen_sharing_intent (from `.symlinks/plugins/listen_sharing_intent/ios`) + - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) + - objective_c (from `.symlinks/plugins/objective_c/ios`) + - open_file_ios (from `.symlinks/plugins/open_file_ios/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - privacy_screen (from `.symlinks/plugins/privacy_screen/ios`) + - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sodium_libs (from `.symlinks/plugins/sodium_libs/ios`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + - ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + +SPEC REPOS: + trunk: + - DKImagePickerController + - DKPhotoGallery + - OrderedSet + - SDWebImage + - Sentry + - SwiftyGif + +EXTERNAL SOURCES: + app_links: + :path: ".symlinks/plugins/app_links/ios" + cupertino_http: + :path: ".symlinks/plugins/cupertino_http/darwin" + device_info_plus: + :path: ".symlinks/plugins/device_info_plus/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" + file_saver: + :path: ".symlinks/plugins/file_saver/ios" + Flutter: + :path: Flutter + flutter_email_sender: + :path: ".symlinks/plugins/flutter_email_sender/ios" + flutter_inappwebview_ios: + :path: ".symlinks/plugins/flutter_inappwebview_ios/ios" + flutter_local_authentication: + :path: ".symlinks/plugins/flutter_local_authentication/ios" + flutter_secure_storage: + :path: ".symlinks/plugins/flutter_secure_storage/ios" + fluttertoast: + :path: ".symlinks/plugins/fluttertoast/ios" + listen_sharing_intent: + :path: ".symlinks/plugins/listen_sharing_intent/ios" + local_auth_darwin: + :path: ".symlinks/plugins/local_auth_darwin/darwin" + objective_c: + :path: ".symlinks/plugins/objective_c/ios" + open_file_ios: + :path: ".symlinks/plugins/open_file_ios/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + privacy_screen: + :path: ".symlinks/plugins/privacy_screen/ios" + sentry_flutter: + :path: ".symlinks/plugins/sentry_flutter/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sodium_libs: + :path: ".symlinks/plugins/sodium_libs/ios" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" + ua_client_hints: + :path: ".symlinks/plugins/ua_client_hints/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + +SPEC CHECKSUMS: + app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6 + cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba + device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 + DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c + DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 + file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 + file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa + flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4 + flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb + flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12 + fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f + listen_sharing_intent: 74a842adcbcf7bedf7bbc938c749da9155141b9a + local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 + objective_c: 77e887b5ba1827970907e10e832eec1683f3431d + open_file_ios: 461db5853723763573e140de3193656f91990d9e + OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e + SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 + Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 + sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b + share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d + SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 + ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe + +PODFILE CHECKSUM: d2d3220ea22664a259778d9e314054751db31361 + +COCOAPODS: 1.16.2 diff --git a/mobile/apps/locker/ios/Runner.xcodeproj/project.pbxproj b/mobile/apps/locker/ios/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..3bda4ae6a4 --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,1027 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; + 27AD87F62E16554700B24C05 /* Share Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 27AD87EC2E16554700B24C05 /* Share Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 4CDDC127876941DC8FF88929 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81319B7747381DEFA0F961F7 /* Pods_Runner.framework */; }; + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 92402655AF53FB304C48E4BC /* Pods_Share_Extension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B8B5400AA6FED21CC714A5A0 /* Pods_Share_Extension.framework */; }; + 9373AE318AB19621F0BB5602 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 871674596693C14F59CB67C9 /* Pods_RunnerTests.framework */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 27AD87F42E16554700B24C05 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 27AD87EB2E16554700B24C05; + remoteInfo = "Share Extension"; + }; + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 97C146E61CF9000F007C117D /* Project object */; + proxyType = 1; + remoteGlobalIDString = 97C146ED1CF9000F007C117D; + remoteInfo = Runner; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 27AD87D02E1647E900B24C05 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 27AD87F62E16554700B24C05 /* Share Extension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 041816B42CE5846482BE66D6 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 27AD87D62E16497400B24C05 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; }; + 27AD87EC2E16554700B24C05 /* Share Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "Share Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 3AEDBF29614F8677DCE69E7E /* Pods-Ente Locker.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Ente Locker.release.xcconfig"; path = "Target Support Files/Pods-Ente Locker/Pods-Ente Locker.release.xcconfig"; sourceTree = ""; }; + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 405BE3630B56B70E48B35F05 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 43F401025D1F7ED737A76C71 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 611BD59A807D8565F74139DC /* Pods_Ente_Locker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Ente_Locker.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 81319B7747381DEFA0F961F7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 871674596693C14F59CB67C9 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 89AC4F9CF1F94E149746EB60 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A1080DB69690A2BD27969FC2 /* Pods-Share Extension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share Extension.profile.xcconfig"; path = "Target Support Files/Pods-Share Extension/Pods-Share Extension.profile.xcconfig"; sourceTree = ""; }; + A68B5D5A3F2AA255856692FC /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + B8B5400AA6FED21CC714A5A0 /* Pods_Share_Extension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Share_Extension.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B96B59C79D8F854FB9838CDF /* Pods-Ente Locker.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Ente Locker.profile.xcconfig"; path = "Target Support Files/Pods-Ente Locker/Pods-Ente Locker.profile.xcconfig"; sourceTree = ""; }; + C775876A93D19660332985CC /* Pods-Ente Locker.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Ente Locker.debug.xcconfig"; path = "Target Support Files/Pods-Ente Locker/Pods-Ente Locker.debug.xcconfig"; sourceTree = ""; }; + D09D02A78DCA8C010704EF91 /* Pods-Share Extension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share Extension.debug.xcconfig"; path = "Target Support Files/Pods-Share Extension/Pods-Share Extension.debug.xcconfig"; sourceTree = ""; }; + E11D72536D63426171C60203 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + F4C835C880E118F472E3A8B3 /* Pods-Share Extension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Share Extension.release.xcconfig"; path = "Target Support Files/Pods-Share Extension/Pods-Share Extension.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 27AD87F72E16554700B24C05 /* Exceptions for "Share Extension" folder in "Share Extension" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Info.plist, + ); + target = 27AD87EB2E16554700B24C05 /* Share Extension */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 27AD87C62E1647E900B24C05 /* Ente Locker */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + ); + explicitFileTypes = { + }; + explicitFolders = ( + ); + path = "Ente Locker"; + sourceTree = ""; + }; + 27AD87ED2E16554700B24C05 /* Share Extension */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 27AD87F72E16554700B24C05 /* Exceptions for "Share Extension" folder in "Share Extension" target */, + ); + explicitFileTypes = { + }; + explicitFolders = ( + ); + path = "Share Extension"; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 27AD87E92E16554700B24C05 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 92402655AF53FB304C48E4BC /* Pods_Share_Extension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CDDC127876941DC8FF88929 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CF47A9397DB3A7B2B1C598E3 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9373AE318AB19621F0BB5602 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C8082294A63A400263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C807B294A618700263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 35603BA520E189E527465052 /* Pods */ = { + isa = PBXGroup; + children = ( + A68B5D5A3F2AA255856692FC /* Pods-Runner.debug.xcconfig */, + 89AC4F9CF1F94E149746EB60 /* Pods-Runner.release.xcconfig */, + 405BE3630B56B70E48B35F05 /* Pods-Runner.profile.xcconfig */, + E11D72536D63426171C60203 /* Pods-RunnerTests.debug.xcconfig */, + 43F401025D1F7ED737A76C71 /* Pods-RunnerTests.release.xcconfig */, + 041816B42CE5846482BE66D6 /* Pods-RunnerTests.profile.xcconfig */, + C775876A93D19660332985CC /* Pods-Ente Locker.debug.xcconfig */, + 3AEDBF29614F8677DCE69E7E /* Pods-Ente Locker.release.xcconfig */, + B96B59C79D8F854FB9838CDF /* Pods-Ente Locker.profile.xcconfig */, + D09D02A78DCA8C010704EF91 /* Pods-Share Extension.debug.xcconfig */, + F4C835C880E118F472E3A8B3 /* Pods-Share Extension.release.xcconfig */, + A1080DB69690A2BD27969FC2 /* Pods-Share Extension.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 27AD87C62E1647E900B24C05 /* Ente Locker */, + 27AD87ED2E16554700B24C05 /* Share Extension */, + 97C146EF1CF9000F007C117D /* Products */, + 331C8082294A63A400263BE5 /* RunnerTests */, + 35603BA520E189E527465052 /* Pods */, + DE91D11FDE3D2B3ABE02BCCD /* Frameworks */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, + 27AD87EC2E16554700B24C05 /* Share Extension.appex */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 27AD87D62E16497400B24C05 /* Runner.entitlements */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, + ); + path = Runner; + sourceTree = ""; + }; + DE91D11FDE3D2B3ABE02BCCD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 81319B7747381DEFA0F961F7 /* Pods_Runner.framework */, + 871674596693C14F59CB67C9 /* Pods_RunnerTests.framework */, + 611BD59A807D8565F74139DC /* Pods_Ente_Locker.framework */, + B8B5400AA6FED21CC714A5A0 /* Pods_Share_Extension.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 27AD87EB2E16554700B24C05 /* Share Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 27AD87F82E16554700B24C05 /* Build configuration list for PBXNativeTarget "Share Extension" */; + buildPhases = ( + 1304177914FB1E5B189BACD6 /* [CP] Check Pods Manifest.lock */, + 27AD87E82E16554700B24C05 /* Sources */, + 27AD87E92E16554700B24C05 /* Frameworks */, + 27AD87EA2E16554700B24C05 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 27AD87ED2E16554700B24C05 /* Share Extension */, + ); + name = "Share Extension"; + productName = "Share Extension"; + productReference = 27AD87EC2E16554700B24C05 /* Share Extension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 331C8080294A63A400263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + AF55335C8D25422D2D282490 /* [CP] Check Pods Manifest.lock */, + 331C807D294A63A400263BE5 /* Sources */, + 331C807F294A63A400263BE5 /* Resources */, + CF47A9397DB3A7B2B1C598E3 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 331C8086294A63A400263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + DA099182B8E4842D754F598F /* [CP] Check Pods Manifest.lock */, + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 9705A1C41CF9048500538489 /* Embed Frameworks */, + 27AD87D02E1647E900B24C05 /* Embed Foundation Extensions */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 82B64A7C257381AA0C0D6B15 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 27AD87F52E16554700B24C05 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1620; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 27AD87EB2E16554700B24C05 = { + CreatedOnToolsVersion = 16.2; + }; + 331C8080294A63A400263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 97C146ED1CF9000F007C117D; + }; + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + LastSwiftMigration = 1100; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + 331C8080294A63A400263BE5 /* RunnerTests */, + 27AD87EB2E16554700B24C05 /* Share Extension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 27AD87EA2E16554700B24C05 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 331C807F294A63A400263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1304177914FB1E5B189BACD6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Share Extension-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + }; + 82B64A7C257381AA0C0D6B15 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; + AF55335C8D25422D2D282490 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + DA099182B8E4842D754F598F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 27AD87E82E16554700B24C05 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 331C807D294A63A400263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 27AD87F52E16554700B24C05 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 27AD87EB2E16554700B24C05 /* Share Extension */; + targetProxy = 27AD87F42E16554700B24C05 /* PBXContainerItemProxy */; + }; + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 97C146ED1CF9000F007C117D /* Runner */; + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 249021D3217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Profile; + }; + 249021D4217E4FDB00AE95B9 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + NEW_SETTING = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Profile; + }; + 27AD87F92E16554700B24C05 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D09D02A78DCA8C010704EF91 /* Pods-Share Extension.debug.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Share Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Share Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "io.ente.locker.Share-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 27AD87FA2E16554700B24C05 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F4C835C880E118F472E3A8B3 /* Pods-Share Extension.release.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Share Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Share Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "io.ente.locker.Share-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 27AD87FB2E16554700B24C05 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A1080DB69690A2BD27969FC2 /* Pods-Share Extension.profile.xcconfig */; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "Share Extension/Share Extension.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Share Extension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "Share Extension"; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "io.ente.locker.Share-Extension"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Profile; + }; + 331C8088294A63A400263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E11D72536D63426171C60203 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Debug; + }; + 331C8089294A63A400263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 43F401025D1F7ED737A76C71 /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Release; + }; + 331C808A294A63A400263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 041816B42CE5846482BE66D6 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; + }; + name = Profile; + }; + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SUPPORTED_PLATFORMS = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + NEW_SETTING = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CUSTOM_GROUP_ID = group.io.ente.locker.share; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + NEW_SETTING = ""; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 27AD87F82E16554700B24C05 /* Build configuration list for PBXNativeTarget "Share Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 27AD87F92E16554700B24C05 /* Debug */, + 27AD87FA2E16554700B24C05 /* Release */, + 27AD87FB2E16554700B24C05 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C8088294A63A400263BE5 /* Debug */, + 331C8089294A63A400263BE5 /* Release */, + 331C808A294A63A400263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + 249021D3217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + 249021D4217E4FDB00AE95B9 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..919434a625 --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/mobile/apps/locker/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/mobile/apps/locker/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..e3773d42e2 --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/ios/Runner.xcworkspace/contents.xcworkspacedata b/mobile/apps/locker/ios/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..21a3cc14c7 --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000000..f9b0d7c5ea --- /dev/null +++ b/mobile/apps/locker/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/mobile/apps/locker/ios/Runner/AppDelegate.swift b/mobile/apps/locker/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000000..626664468b --- /dev/null +++ b/mobile/apps/locker/ios/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d36b1fab2d --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9ada4725e9b0ddb1deab583e5b5102493aa332 GIT binary patch literal 10932 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..797d452e458972bab9d994556c8305db4c827017 GIT binary patch literal 406 zcmV;H0crk;P))>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..6ed2d933e1120817fe9182483a228007b18ab6ae GIT binary patch literal 450 zcmV;z0X_bSP)iGWQ_5NJQ_~rNh*z)}eT%KUb z`7gNk0#AwF^#0T0?hIa^`~Ck;!}#m+_uT050aTR(J!bU#|IzRL%^UsMS#KsYnTF*!YeDOytlP4VhV?b} z%rz_<=#CPc)tU1MZTq~*2=8~iZ!lSa<{9b@2Jl;?IEV8)=fG217*|@)CCYgFze-x? zIFODUIA>nWKpE+bn~n7;-89sa>#DR>TSlqWk*!2hSN6D~Qb#VqbP~4Fk&m`@1$JGr zXPIdeRE&b2Thd#{MtDK$px*d3-Wx``>!oimf%|A-&-q*6KAH)e$3|6JV%HX{Hig)k suLT-RhftRq8b9;(V=235Wa|I=027H2wCDra;{X5v07*qoM6N<$f;9x^2LJ#7 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd7b0099ca80c806f8fe495613e8d6c69460d76 GIT binary patch literal 282 zcmV+#0p(^bcu7P-R4C8Q z&e;xxFbF_Vrezo%_kH*OKhshZ6BFpG-Y1e10`QXJKbND7AMQ&cMj60B5TNObaZxYybcN07*qoM6N<$g3m;S%K!iX literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fe730945a01f64a61e2235dbe3f45b08f7729182 GIT binary patch literal 462 zcmV;<0WtoGP)-}iV`2<;=$?g5M=KQbZ{F&YRNy7Nn@%_*5{gvDM0aKI4?ESmw z{NnZg)A0R`+4?NF_RZexyVB&^^ZvN!{I28tr{Vje;QNTz`dG&Jz0~Ek&f2;*Z7>B|cg}xYpxEFY+0YrKLF;^Q+-HreN0P{&i zK~zY`?b7ECf-n?@;d<&orQ*Q7KoR%4|C>{W^h6@&01>0SKS`dn{Q}GT%Qj_{PLZ_& zs`MFI#j-(>?bvdZ!8^xTwlY{qA)T4QLbY@j(!YJ7aXJervHy6HaG_2SB`6CC{He}f zHVw(fJWApwPq!6VY7r1w-Fs)@ox~N+q|w~e;JI~C4Vf^@d>Wvj=fl`^u9x9wd9 zR%3*Q+)t%S!MU_`id^@&Y{y7-r98lZX0?YrHlfmwb?#}^1b{8g&KzmkE(L>Z&)179 zp<)v6Y}pRl100G2FL_t(o!|l{-Q-VMg#&MKg7c{O0 z2wJImOS3Gy*Z2Qifdv~JYOp;v+U)a|nLoc7hNH;I$;lzDt$}rkaFw1mYK5_0Q(Sut zvbEloxON7$+HSOgC9Z8ltuC&0OSF!-mXv5caV>#bc3@hBPX@I$58-z}(ZZE!t-aOG zpjNkbau@>yEzH(5Yj4kZiMH32XI!4~gVXNnjAvRx;Sdg^`>2DpUEwoMhTs_st8pKG z(%SHyHdU&v%f36~uERh!bd`!T2dw;z6PrOTQ7Vt*#9F2uHlUVnb#ev_o^fh}Dzmq} zWtlk35}k=?xj28uO|5>>$yXadTUE@@IPpgH`gJ~Ro4>jd1IF|(+IX>8M4Ps{PNvmI zNj4D+XgN83gPt_Gm}`Ybv{;+&yu-C(Grdiahmo~BjG-l&mWM+{e5M1sm&=xduwgM9 z`8OEh`=F3r`^E{n_;%9weN{cf2%7=VzC@cYj+lg>+3|D|_1C@{hcU(DyQG_BvBWe? zvTv``=%b1zrol#=R`JB)>cdjpWt&rLJgVp-t?DREyuq1A%0Z4)6_WsQ7{nzjN zo!X zGXV)2i3kcZIL~_j>uIKPK_zib+3T+Nt3Mb&Br)s)UIaA}@p{wDda>7=Q|mGRp7pqY zkJ!7E{MNz$9nOwoVqpFb)}$IP24Wn2JJ=Cw(!`OXJBr45rP>>AQr$6c7slJWvbpNW z@KTwna6d?PP>hvXCcp=4F;=GR@R4E7{4VU^0p4F>v^#A|>07*qoM6N<$f*5nx ACIA2c literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..502f463a9bc882b461c96aadf492d1729e49e725 GIT binary patch literal 586 zcmV-Q0=4~#P)+}#`wDE{8-2Mebf5<{{PqV{TgVcv*r8?UZ3{-|G?_}T*&y;@cqf{ z{Q*~+qr%%p!1pS*_Uicl#q9lc(D`!D`LN62sNwq{oYw(Wmhk)k<@f$!$@ng~_5)Ru z0Z)trIA5^j{DIW^c+vT2%lW+2<(RtE2wR;4O@)Tm`Xr*?A(qYoM}7i5Yxw>D(&6ou zxz!_Xr~yNF+waPe00049Nkl*;a!v6h%{rlvIH#gW3s8p;bFr=l}mRqpW2h zw=OA%hdyL~z+UHOzl0eKhEr$YYOL-c-%Y<)=j?(bzDweB7{b+%_ypvm_cG{SvM=DK zhv{K@m>#Bw>2W$eUI#iU)Wdgs8Y3U+A$Gd&{+j)d)BmGKx+43U_!tik_YlN)>$7G! zhkE!s;%oku3;IwG3U^2kw?z+HM)jB{@zFhK8P#KMSytSthr+4!c(5c%+^UBn`0X*2 zy3(k600_CSZj?O$Qu%&$;|TGUJrptR(HzyIx>5E(2r{eA(<6t3e3I0B)7d6s7?Z5J zZ!rtKvA{MiEBm&KFtoifx>5P^Z=vl)95XJn()aS5%ad(s?4-=Tkis9IGu{`Fy8r+H07*qoM6N<$f20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0ec303439225b78712f49115768196d8d76f6790 GIT binary patch literal 862 zcmV-k1EKthP)20Z)wqMt%V?S?~D#06};F zA3KcL`Wb+>5ObvgQIG&ig8(;V04hz?@cqy3{mSh8o!|U|)cI!1_+!fWH@o*8vh^CU z^ws0;(c$gI+2~q^tO#GDHf@=;DncUw00J^eL_t(&-tE|HQ`%4vfZ;WsBqu-$0nu1R zq^Vj;p$clf^?twn|KHO+IGt^q#a3X?w9dXC@*yxhv&l}F322(8Y1&=P&I}~G@#h6; z1CV9ecD9ZEe87{{NtI*)_aJ<`kJa z?5=RBtFF50s;jQLFil-`)m2wrb=6h(&brpj%nG_U&ut~$?8Rokzxi8zJoWr#2dto5 zOX_URcc<1`Iky+jc;A%Vzx}1QU{2$|cKPom2Vf1{8m`vja4{F>HS?^Nc^rp}xo+Nh zxd}eOm`fm3@MQC1< zIk&aCjb~Yh%5+Yq0`)D;q{#-Uqlv*o+Oor zE!I71Z@ASH3grl8&P^L0WpavHoP|UX4e?!igT`4?AZk$hu*@%6WJ;zDOGlw7kj@ zY5!B-0ft0f?Lgb>C;$Ke07*qoM6N<$f~t1N9smFU literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..e9f5fea27c705180eb716271f41b582e76dcbd90 GIT binary patch literal 1674 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8 GIT binary patch literal 1418 zcmV;51$Fv~P)q zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+ zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq z^={4hPQv)y=I|4n+?>7Fim=dxt1 z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf` zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_> z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3 zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62( zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;? zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-< z{s<&cCV_1`^TD^ia9!*mQDq& zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000000..0bedcf2fd4 --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838 GIT binary patch literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000000..89c2725b70 --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/mobile/apps/locker/ios/Runner/Base.lproj/LaunchScreen.storyboard b/mobile/apps/locker/ios/Runner/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..f2e259c7c9 --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/ios/Runner/Base.lproj/Main.storyboard b/mobile/apps/locker/ios/Runner/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..f3c28516fb --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/ios/Runner/Info.plist b/mobile/apps/locker/ios/Runner/Info.plist new file mode 100644 index 0000000000..836320f49d --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Info.plist @@ -0,0 +1,68 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Ente Locker + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Ente Locker + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + + NSCameraUsageDescription + Please allow access to camera so that Ente can scan and backup your documents. + NSPhotoLibraryUsageDescription + Please allow access to your library so that Ente can encrypt and back up your documents. + NSPhotoLibraryAddUsageDescription + Please allow access to your library so that Ente can encrypt and back up your documents. + AppGroupId + $(CUSTOM_GROUP_ID) + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLSchemes + + ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER) + + + + + diff --git a/mobile/apps/locker/ios/Runner/Runner-Bridging-Header.h b/mobile/apps/locker/ios/Runner/Runner-Bridging-Header.h new file mode 100644 index 0000000000..308a2a560b --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Runner-Bridging-Header.h @@ -0,0 +1 @@ +#import "GeneratedPluginRegistrant.h" diff --git a/mobile/apps/locker/ios/Runner/Runner.entitlements b/mobile/apps/locker/ios/Runner/Runner.entitlements new file mode 100644 index 0000000000..b79c362eed --- /dev/null +++ b/mobile/apps/locker/ios/Runner/Runner.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.io.ente.locker.share + + + diff --git a/mobile/apps/locker/ios/RunnerTests/RunnerTests.swift b/mobile/apps/locker/ios/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..86a7c3b1b6 --- /dev/null +++ b/mobile/apps/locker/ios/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Flutter +import UIKit +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/mobile/apps/locker/ios/Share Extension/Base.lproj/MainInterface.storyboard b/mobile/apps/locker/ios/Share Extension/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000000..286a50894d --- /dev/null +++ b/mobile/apps/locker/ios/Share Extension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/ios/Share Extension/Info.plist b/mobile/apps/locker/ios/Share Extension/Info.plist new file mode 100644 index 0000000000..1efefe2ea7 --- /dev/null +++ b/mobile/apps/locker/ios/Share Extension/Info.plist @@ -0,0 +1,33 @@ + + + + + AppGroupId + $(CUSTOM_GROUP_ID) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + NSExtension + + NSExtensionAttributes + + NSExtensionActivationRule + SUBQUERY ( + extensionItems, + $extensionItem, + SUBQUERY ( + $extensionItem.attachments, + $attachment, + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.data" AND + !(ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.text" OR + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" OR + ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.utf8-plain-text") + ).@count == 1 + ).@count == 1 + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.share-services + + + diff --git a/mobile/apps/locker/ios/Share Extension/Share Extension.entitlements b/mobile/apps/locker/ios/Share Extension/Share Extension.entitlements new file mode 100644 index 0000000000..b79c362eed --- /dev/null +++ b/mobile/apps/locker/ios/Share Extension/Share Extension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.io.ente.locker.share + + + diff --git a/mobile/apps/locker/ios/Share Extension/ShareViewController.swift b/mobile/apps/locker/ios/Share Extension/ShareViewController.swift new file mode 100644 index 0000000000..f5aabe3e2a --- /dev/null +++ b/mobile/apps/locker/ios/Share Extension/ShareViewController.swift @@ -0,0 +1,10 @@ +import listen_sharing_intent + +class ShareViewController: RSIShareViewController { + + // Use this method to return false if you don't want to redirect to host app automatically. + // Default is true + override func shouldAutoRedirect() -> Bool { + return true + } +} diff --git a/mobile/apps/locker/l10n.yaml b/mobile/apps/locker/l10n.yaml new file mode 100644 index 0000000000..15338f2ddc --- /dev/null +++ b/mobile/apps/locker/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart diff --git a/mobile/apps/locker/lib/app.dart b/mobile/apps/locker/lib/app.dart new file mode 100644 index 0000000000..05aea2ae6d --- /dev/null +++ b/mobile/apps/locker/lib/app.dart @@ -0,0 +1,220 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:adaptive_theme/adaptive_theme.dart'; +import 'package:ente_accounts/services/user_service.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/signed_in_event.dart'; +import 'package:ente_events/models/signed_out_event.dart'; +import 'package:ente_strings/l10n/strings_localizations.dart'; +import 'package:ente_ui/theme/colors.dart'; +import 'package:ente_ui/theme/ente_theme_data.dart'; +import 'package:ente_ui/utils/window_listener_service.dart'; +import 'package:flutter/foundation.dart'; +import "package:flutter/material.dart"; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:locker/core/locale.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/ui/pages/home_page.dart'; +import 'package:locker/ui/pages/onboarding_page.dart'; +import 'package:tray_manager/tray_manager.dart'; +import 'package:window_manager/window_manager.dart'; + +class App extends StatefulWidget { + final Locale? locale; + const App({super.key, this.locale = const Locale("en")}); + + static void setLocale(BuildContext context, Locale newLocale) { + final _AppState state = context.findAncestorStateOfType<_AppState>()!; + state.setLocale(newLocale); + } + + @override + State createState() => _AppState(); +} + +class _AppState extends State + with WindowListener, TrayListener, WidgetsBindingObserver { + late StreamSubscription _signedOutEvent; + late StreamSubscription _signedInEvent; + Locale? locale; + setLocale(Locale newLocale) { + setState(() { + locale = newLocale; + }); + } + + Future initWindowManager() async { + windowManager.addListener(this); + } + + Future initTrayManager() async { + trayManager.addListener(this); + } + + @override + void initState() { + initWindowManager(); + initTrayManager(); + WidgetsBinding.instance.addObserver(this); + + _signedOutEvent = Bus.instance.on().listen((event) { + if (mounted) { + setState(() {}); + } + }); + _signedInEvent = Bus.instance.on().listen((event) { + UserService.instance.getUserDetailsV2().ignore(); + if (mounted) { + setState(() {}); + } + }); + locale = widget.locale; + super.initState(); + } + + @override + void dispose() { + super.dispose(); + + windowManager.removeListener(this); + trayManager.removeListener(this); + + _signedOutEvent.cancel(); + _signedInEvent.cancel(); + } + + @override + Widget build(BuildContext context) { + final schemes = ColorSchemeBuilder.fromCustomColors( + primary700: const Color(0xFF1565C0), // Dark blue + primary500: const Color(0xFF2196F3), // Material blue + primary400: const Color(0xFF42A5F5), // Light blue + primary300: const Color(0xFF90CAF9), // Very light blue + iconButtonColor: const Color(0xFF1976D2), // Custom icon color + gradientButtonBgColors: const [ + Color(0xFF1565C0), + Color(0xFF2196F3), + Color(0xFF42A5F5), + ], + ); + + final lightTheme = createAppThemeData( + brightness: Brightness.light, + colorScheme: schemes.light, + ); + + final darkTheme = createAppThemeData( + brightness: Brightness.dark, + colorScheme: schemes.dark, + ); + + Widget buildApp() { + if (Platform.isAndroid || + Platform.isWindows || + Platform.isLinux || + kDebugMode) { + return AdaptiveTheme( + light: lightTheme, + dark: darkTheme, + initial: AdaptiveThemeMode.system, + builder: (lightTheme, dartTheme) => MaterialApp( + title: "ente", + themeMode: ThemeMode.system, + theme: lightTheme, + darkTheme: dartTheme, + debugShowCheckedModeBanner: false, + locale: locale, + supportedLocales: appSupportedLocales, + localeListResolutionCallback: localResolutionCallBack, + localizationsDelegates: const [ + AppLocalizations.delegate, + StringsLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + routes: _getRoutes, + ), + ); + } else { + return MaterialApp( + title: "ente", + themeMode: ThemeMode.system, + theme: lightTheme, + darkTheme: darkTheme, + debugShowCheckedModeBanner: false, + locale: locale, + supportedLocales: appSupportedLocales, + localeListResolutionCallback: localResolutionCallBack, + localizationsDelegates: const [ + AppLocalizations.delegate, + StringsLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + routes: _getRoutes, + ); + } + } + + // Wrap the app with MediaQuery to control text scaling and ensure + // consistent font sizes across Android and iOS + return MediaQuery.withClampedTextScaling( + minScaleFactor: 0.8, + maxScaleFactor: 1.3, + child: buildApp(), + ); + } + + Map get _getRoutes { + return { + "/": (context) => Configuration.instance.hasConfiguredAccount() + ? const HomePage() + : const OnboardingPage(), + }; + } + + @override + void onWindowResize() { + WindowListenerService.instance.onWindowResize().ignore(); + } + + @override + void onTrayIconMouseDown() { + if (Platform.isWindows) { + windowManager.show(); + } else { + trayManager.popUpContextMenu(); + } + } + + @override + void onTrayIconRightMouseDown() { + if (Platform.isWindows) { + trayManager.popUpContextMenu(); + } else { + windowManager.show(); + } + } + + @override + void onTrayIconRightMouseUp() {} + + @override + void onTrayMenuItemClick(MenuItem menuItem) { + switch (menuItem.key) { + case 'hide_window': + windowManager.hide(); + break; + case 'show_window': + windowManager.show(); + break; + case 'exit_app': + windowManager.destroy(); + break; + } + } +} diff --git a/mobile/apps/locker/lib/core/constants.dart b/mobile/apps/locker/lib/core/constants.dart new file mode 100644 index 0000000000..b1ff31d0cb --- /dev/null +++ b/mobile/apps/locker/lib/core/constants.dart @@ -0,0 +1,45 @@ +import 'package:flutter/foundation.dart'; + +const String kAccountsUrl = "https://accounts.ente.io"; +const int microSecondsInDay = 86400000000; +const int android11SDKINT = 30; + +// 256 bit key maps to 24 words +// https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#Generating_the_mnemonic +const mnemonicKeyWordCount = 24; + +const kDefaultProductionEndpoint = 'https://api.ente.io'; + +final tempDirCleanUpInterval = kDebugMode + ? const Duration(hours: 1).inMicroseconds + : const Duration(hours: 6).inMicroseconds; + +const uploadTempFilePrefix = "upload_file_"; + +const blackThumbnailBase64 = '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB' + 'AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQ' + 'EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/wAARC' + 'ACWASwDAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUF' + 'BAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk' + '6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztL' + 'W2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAA' + 'AAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVY' + 'nLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImK' + 'kpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oAD' + 'AMBAAIRAxEAPwD/AD/6ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAC' + 'gAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' + 'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACg' + 'AoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKA' + 'CgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAK' + 'ACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA' + 'KACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo' + 'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/9k='; diff --git a/mobile/apps/locker/lib/core/errors.dart b/mobile/apps/locker/lib/core/errors.dart new file mode 100644 index 0000000000..6722276ad3 --- /dev/null +++ b/mobile/apps/locker/lib/core/errors.dart @@ -0,0 +1,41 @@ +class UnauthorizedError extends Error {} + +class PassKeySessionNotVerifiedError extends Error {} + +class PassKeySessionExpiredError extends Error {} + +class SrpSetupNotCompleteError extends Error {} + +class StorageLimitExceededError extends Error {} + +class NoActiveSubscriptionError extends Error {} + +// error when file size + current usage >= storage plan limit + buffer +class FileTooLargeForPlanError extends Error {} + +class WiFiUnavailableError extends Error {} + +class SilentlyCancelUploadsError extends Error {} + +class InvalidFileError extends ArgumentError { + final InvalidReason reason; + + InvalidFileError(String super.message, this.reason); + + @override + String toString() { + return 'InvalidFileError: $message (reason: $reason)'; + } +} + +enum InvalidReason { + assetDeleted, + assetDeletedEvent, + sourceFileMissing, + livePhotoToImageTypeChanged, + imageToLivePhotoTypeChanged, + livePhotoVideoMissing, + thumbnailMissing, + tooLargeFile, + unknown, +} diff --git a/mobile/apps/locker/lib/core/locale.dart b/mobile/apps/locker/lib/core/locale.dart new file mode 100644 index 0000000000..7fc71a1eda --- /dev/null +++ b/mobile/apps/locker/lib/core/locale.dart @@ -0,0 +1,74 @@ +import 'package:flutter/cupertino.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +// 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'), +]; + +Locale? autoDetectedLocale; +// This function takes device locales and supported locales as input +// and returns the best matching locale. +// The device locales are sorted by priority, so the first one is the most preferred. +Locale localResolutionCallBack(onDeviceLocales, supportedLocales) { + final Set languageSupport = {}; + for (Locale supportedLocale in appSupportedLocales) { + languageSupport.add(supportedLocale.languageCode); + } + for (Locale locale in onDeviceLocales) { + // check if exact local is supported, if yes, return it + if (appSupportedLocales.contains(locale)) { + autoDetectedLocale = locale; + return locale; + } + // check if language code is supported, if yes, return it + if (languageSupport.contains(locale.languageCode)) { + autoDetectedLocale = locale; + return locale; + } + } + // Return the first language code match or default to 'en' + return autoDetectedLocale ?? const Locale('en'); +} + +Future getLocale({ + bool noFallback = false, +}) async { + final String? savedValue = + (await SharedPreferences.getInstance()).getString('locale'); + // if savedLocale is not null and is supported by the app, return it + if (savedValue != null) { + late Locale savedLocale; + if (savedValue.contains('_')) { + final List parts = savedValue.split('_'); + savedLocale = Locale(parts[0], parts[1]); + } else { + savedLocale = Locale(savedValue); + } + if (appSupportedLocales.contains(savedLocale)) { + return savedLocale; + } + } + if (autoDetectedLocale != null) { + return autoDetectedLocale!; + } + if (noFallback) { + return null; + } + return const Locale('en'); +} + +Future setLocale(Locale locale) async { + if (!appSupportedLocales.contains(locale)) { + throw Exception('Locale $locale is not supported by the app'); + } + final StringBuffer out = StringBuffer(locale.languageCode); + if (locale.countryCode != null && locale.countryCode!.isNotEmpty) { + out.write('_'); + out.write(locale.countryCode); + } + await (await SharedPreferences.getInstance()) + .setString('locale', out.toString()); +} diff --git a/mobile/apps/locker/lib/events/backup_updated_event.dart b/mobile/apps/locker/lib/events/backup_updated_event.dart new file mode 100644 index 0000000000..d4dd91e44a --- /dev/null +++ b/mobile/apps/locker/lib/events/backup_updated_event.dart @@ -0,0 +1,10 @@ +import "dart:collection"; + +import "package:ente_events/models/event.dart"; +import "package:locker/services/files/upload/models/backup_item.dart"; + +class BackupUpdatedEvent extends Event { + final LinkedHashMap items; + + BackupUpdatedEvent(this.items); +} diff --git a/mobile/apps/locker/lib/events/collections_updated_event.dart b/mobile/apps/locker/lib/events/collections_updated_event.dart new file mode 100644 index 0000000000..d35e3a15cf --- /dev/null +++ b/mobile/apps/locker/lib/events/collections_updated_event.dart @@ -0,0 +1,3 @@ +import 'package:ente_events/models/event.dart'; + +class CollectionsUpdatedEvent extends Event {} diff --git a/mobile/apps/locker/lib/l10n/app_en.arb b/mobile/apps/locker/lib/l10n/app_en.arb new file mode 100644 index 0000000000..0d05f8ec45 --- /dev/null +++ b/mobile/apps/locker/lib/l10n/app_en.arb @@ -0,0 +1,312 @@ +{ + "onBoardingBody": "Securely backup your documents", + "newUser": "New to Ente", + "existingUser": "Existing User", + "developerSettings": "Developer settings", + "yes": "Yes", + "developerSettingsWarning": "Are you sure that you want to modify Developer settings?", + "serverEndpoint": "Server endpoint", + "endpointUpdatedMessage": "Endpoint updated successfully", + "invalidEndpoint": "Invalid endpoint", + "invalidEndpointMessage": "Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.", + "saveAction": "Save", + "untitled": "Untitled", + "edit": "Edit", + "delete": "Delete", + "noFilesFound": "No files here", + "addFiles": "Add Files", + "name": "Name", + "date": "Date", + "pleaseWait": "Please wait...", + "downloading": "Downloading...", + "downloadingProgress": "Downloading... {percentage}%", + "@downloadingProgress": { + "description": "Download progress message", + "placeholders": { + "percentage": { + "type": "int", + "example": "75" + } + } + }, + "downloadFailed": "Download Failed", + "failedToDownloadOrDecrypt": "Failed to download or decrypt file", + "errorOpeningFile": "Error opening file", + "errorOpeningFileMessage": "Error opening file: {error}", + "@errorOpeningFileMessage": { + "description": "Error message when opening a file fails", + "placeholders": { + "error": { + "type": "String", + "example": "File not found" + } + } + }, + "couldNotOpenFile": "Could not open file: {error}", + "@couldNotOpenFile": { + "description": "Error message when file cannot be opened", + "placeholders": { + "error": { + "type": "String", + "example": "Permission denied" + } + } + }, + "minutesAgo": "{minutes}m ago", + "@minutesAgo": { + "description": "Time format for minutes ago", + "placeholders": { + "minutes": { + "type": "int", + "example": "2" + } + } + }, + "hoursAgo": "{hours}h ago", + "@hoursAgo": { + "description": "Time format for hours ago", + "placeholders": { + "hours": { + "type": "int", + "example": "2" + } + } + }, + "yesterday": "Yesterday", + "daysAgo": "{days}d ago", + "@daysAgo": { + "description": "Time format for days ago", + "placeholders": { + "days": { + "type": "int", + "example": "5" + } + } + }, + "collectionRenamedSuccessfully": "Collection renamed successfully", + "failedToRenameCollection": "Failed to rename collection: {error}", + "@failedToRenameCollection": { + "description": "Error message when collection rename fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "collectionCannotBeDeleted": "This collection cannot be deleted", + "deleteCollection": "Delete collection", + "deleteCollectionConfirmation": "Are you sure you want to delete \"{collectionName}\"?", + "@deleteCollectionConfirmation": { + "description": "Confirmation message for deleting a collection", + "placeholders": { + "collectionName": { + "type": "String", + "example": "My Collection" + } + } + }, + "cancel": "Cancel", + "collectionDeletedSuccessfully": "Collection deleted successfully", + "failedToDeleteCollection": "Failed to delete collection: {error}", + "@failedToDeleteCollection": { + "description": "Error message when collection deletion fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "collections": "Collections", + "retry": "Retry", + "failedToSyncTrash": "Failed to sync trash: {error}", + "@failedToSyncTrash": { + "description": "Error message when trash sync fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "pleaseSelectAtLeastOneCollection": "Please select at least one collection", + "unknownItemType": "Unknown item type", + "fileDeletedSuccessfully": "File deleted successfully", + "failedToDeleteFile": "Failed to delete file: {error}", + "@failedToDeleteFile": { + "description": "Error message when file deletion fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "fileUpdatedSuccessfully": "File updated successfully!", + "failedToUpdateFile": "Failed to update file: {error}", + "@failedToUpdateFile": { + "description": "Error message when file update fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "noChangesWereMade": "No changes were made", + "noCollectionsAvailableForRestore": "No collections available for restore", + "trashClearedSuccessfully": "Trash cleared successfully", + "failedToClearTrash": "Failed to clear trash: {error}", + "@failedToClearTrash": { + "description": "Error message when clearing trash fails", + "placeholders": { + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "trash": "Trash", + "deletedPermanently": "Deleted \"{fileName}\" permanently", + "@deletedPermanently": { + "description": "Message when file is permanently deleted from trash", + "placeholders": { + "fileName": { + "type": "String", + "example": "photo.jpg" + } + } + }, + "failedToRestoreFile": "Failed to restore \"{fileName}\": {error}", + "@failedToRestoreFile": { + "description": "Error message when file restoration fails", + "placeholders": { + "fileName": { + "type": "String", + "example": "photo.jpg" + }, + "error": { + "type": "String", + "example": "Network error" + } + } + }, + "createNewCollection": "Create new collection", + "create": "Create", + "renameCollection": "Rename collection", + "save": "Save", + "deleteFile": "Delete File", + "deleteFileConfirmation": "Are you sure you want to delete \"{fileName}\"?", + "@deleteFileConfirmation": { + "description": "Confirmation message for file deletion", + "placeholders": { + "fileName": { + "type": "String", + "example": "photo.jpg" + } + } + }, + "noCollectionsFound": "No collections found", + "createYourFirstCollection": "Create your first collection to get started", + "createCollection": "Create Collection", + "nothingYet": "Nothing yet", + "uploadYourFirstDocument": "Upload your first document to get started", + "uploadDocument": "Upload Document", + "items": "{count} items", + "@items": { + "description": "Number of items in a collection", + "placeholders": { + "count": { + "type": "int", + "example": "5" + } + } + }, + "files": "{count, plural, =0{no files} =1{1 file} other{{count} files}}", + "@files": { + "description": "Number of files in a collection", + "placeholders": { + "count": { + "type": "int", + "example": "5" + } + } + }, + "createCollectionTooltip": "Create collection", + "uploadDocumentTooltip": "Upload document", + "restore": "Restore", + "restoreFile": "Restore {fileName}", + "@restoreFile": { + "description": "Dialog title for restoring a file from trash", + "placeholders": { + "fileName": { + "type": "String", + "example": "photo.jpg" + } + } + }, + "noCollectionsAvailable": "No collections available", + "emptyTrash": "Empty trash", + "emptyTrashTooltip": "Empty trash", + "emptyTrashConfirmation": "Are you sure you want to permanently delete all items in trash? This action cannot be undone.", + "fileTitle": "File title", + "note": "Note", + "optionalNote": "Optional note", + "upload": "Upload", + "documentsHint": "Documents", + "searchHint": "Search...", + "noCollectionsFoundForQuery": "No collections found for \"{query}\"", + "@noCollectionsFoundForQuery": { + "description": "Message when no collections match the search query", + "placeholders": { + "query": { + "type": "String", + "example": "vacation" + } + } + }, + "uncategorized": "Uncategorized", + "syncingTrash": "Syncing trash...", + "restoring": "Restoring...", + "restoredFileToCollection": "Restored \"{fileName}\" to \"{collectionName}\"", + "@restoredFileToCollection": { + "description": "Success message when file is restored to a collection", + "placeholders": { + "fileName": { + "type": "String", + "example": "photo.jpg" + }, + "collectionName": { + "type": "String", + "example": "Vacation Photos" + } + } + }, + "clearingTrash": "Clearing trash...", + "trashIsEmpty": "Trash is empty", + "share": "Share", + "shareLink": "Share link", + "creatingShareLink": "Creating link...", + "shareThisLink": "Anyone with this link can access your file.", + "copyLink": "Copy link", + "deleteLink": "Delete link", + "close": "Close", + "linkCopiedToClipboard": "Link copied to clipboard", + "deleteShareLink": "Delete link", + "deleteShareLinkDialogTitle": "Delete link?", + "deleteShareLinkConfirmation": "People with this link will no longer be able to access the file.", + "deletingShareLink": "Deleting link...", + "shareLinkDeletedSuccessfully": "Link deleted successfully", + "failedToCreateShareLink": "Failed to create link", + "failedToDeleteShareLink": "Failed to delete link", + "deletingFile": "Deleting file...", + "addInformation": "Add information", + "addInformationDialogSubtitle": "Choose the type of information you want to add", + "physicalDocument": "Physical document", + "physicalDocumentDescription": "Save information about documents and items in the real world.", + "emergencyContact": "Emergency contact", + "emergencyContactDescription": "Save information about important contacts.", + "accountCredential": "Account credential", + "accountCredentialDescription": "Save information about your important account credentials." +} diff --git a/mobile/apps/locker/lib/l10n/app_localizations.dart b/mobile/apps/locker/lib/l10n/app_localizations.dart new file mode 100644 index 0000000000..5b6d76b735 --- /dev/null +++ b/mobile/apps/locker/lib/l10n/app_localizations.dart @@ -0,0 +1,805 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_en.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations? of(BuildContext context) { + return Localizations.of(context, AppLocalizations); + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [Locale('en')]; + + /// No description provided for @onBoardingBody. + /// + /// In en, this message translates to: + /// **'Securely backup your documents'** + String get onBoardingBody; + + /// No description provided for @newUser. + /// + /// In en, this message translates to: + /// **'New to Ente'** + String get newUser; + + /// No description provided for @existingUser. + /// + /// In en, this message translates to: + /// **'Existing User'** + String get existingUser; + + /// No description provided for @developerSettings. + /// + /// In en, this message translates to: + /// **'Developer settings'** + String get developerSettings; + + /// No description provided for @yes. + /// + /// In en, this message translates to: + /// **'Yes'** + String get yes; + + /// No description provided for @developerSettingsWarning. + /// + /// In en, this message translates to: + /// **'Are you sure that you want to modify Developer settings?'** + String get developerSettingsWarning; + + /// No description provided for @serverEndpoint. + /// + /// In en, this message translates to: + /// **'Server endpoint'** + String get serverEndpoint; + + /// No description provided for @endpointUpdatedMessage. + /// + /// In en, this message translates to: + /// **'Endpoint updated successfully'** + String get endpointUpdatedMessage; + + /// No description provided for @invalidEndpoint. + /// + /// In en, this message translates to: + /// **'Invalid endpoint'** + String get invalidEndpoint; + + /// No description provided for @invalidEndpointMessage. + /// + /// In en, this message translates to: + /// **'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'** + String get invalidEndpointMessage; + + /// No description provided for @saveAction. + /// + /// In en, this message translates to: + /// **'Save'** + String get saveAction; + + /// No description provided for @untitled. + /// + /// In en, this message translates to: + /// **'Untitled'** + String get untitled; + + /// No description provided for @edit. + /// + /// In en, this message translates to: + /// **'Edit'** + String get edit; + + /// No description provided for @delete. + /// + /// In en, this message translates to: + /// **'Delete'** + String get delete; + + /// No description provided for @noFilesFound. + /// + /// In en, this message translates to: + /// **'No files here'** + String get noFilesFound; + + /// No description provided for @addFiles. + /// + /// In en, this message translates to: + /// **'Add Files'** + String get addFiles; + + /// No description provided for @name. + /// + /// In en, this message translates to: + /// **'Name'** + String get name; + + /// No description provided for @date. + /// + /// In en, this message translates to: + /// **'Date'** + String get date; + + /// No description provided for @pleaseWait. + /// + /// In en, this message translates to: + /// **'Please wait...'** + String get pleaseWait; + + /// No description provided for @downloading. + /// + /// In en, this message translates to: + /// **'Downloading...'** + String get downloading; + + /// Download progress message + /// + /// In en, this message translates to: + /// **'Downloading... {percentage}%'** + String downloadingProgress(int percentage); + + /// No description provided for @downloadFailed. + /// + /// In en, this message translates to: + /// **'Download Failed'** + String get downloadFailed; + + /// No description provided for @failedToDownloadOrDecrypt. + /// + /// In en, this message translates to: + /// **'Failed to download or decrypt file'** + String get failedToDownloadOrDecrypt; + + /// No description provided for @errorOpeningFile. + /// + /// In en, this message translates to: + /// **'Error opening file'** + String get errorOpeningFile; + + /// Error message when opening a file fails + /// + /// In en, this message translates to: + /// **'Error opening file: {error}'** + String errorOpeningFileMessage(String error); + + /// Error message when file cannot be opened + /// + /// In en, this message translates to: + /// **'Could not open file: {error}'** + String couldNotOpenFile(String error); + + /// Time format for minutes ago + /// + /// In en, this message translates to: + /// **'{minutes}m ago'** + String minutesAgo(int minutes); + + /// Time format for hours ago + /// + /// In en, this message translates to: + /// **'{hours}h ago'** + String hoursAgo(int hours); + + /// No description provided for @yesterday. + /// + /// In en, this message translates to: + /// **'Yesterday'** + String get yesterday; + + /// Time format for days ago + /// + /// In en, this message translates to: + /// **'{days}d ago'** + String daysAgo(int days); + + /// No description provided for @collectionRenamedSuccessfully. + /// + /// In en, this message translates to: + /// **'Collection renamed successfully'** + String get collectionRenamedSuccessfully; + + /// Error message when collection rename fails + /// + /// In en, this message translates to: + /// **'Failed to rename collection: {error}'** + String failedToRenameCollection(String error); + + /// No description provided for @collectionCannotBeDeleted. + /// + /// In en, this message translates to: + /// **'This collection cannot be deleted'** + String get collectionCannotBeDeleted; + + /// No description provided for @deleteCollection. + /// + /// In en, this message translates to: + /// **'Delete collection'** + String get deleteCollection; + + /// Confirmation message for deleting a collection + /// + /// In en, this message translates to: + /// **'Are you sure you want to delete \"{collectionName}\"?'** + String deleteCollectionConfirmation(String collectionName); + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @collectionDeletedSuccessfully. + /// + /// In en, this message translates to: + /// **'Collection deleted successfully'** + String get collectionDeletedSuccessfully; + + /// Error message when collection deletion fails + /// + /// In en, this message translates to: + /// **'Failed to delete collection: {error}'** + String failedToDeleteCollection(String error); + + /// No description provided for @collections. + /// + /// In en, this message translates to: + /// **'Collections'** + String get collections; + + /// No description provided for @retry. + /// + /// In en, this message translates to: + /// **'Retry'** + String get retry; + + /// Error message when trash sync fails + /// + /// In en, this message translates to: + /// **'Failed to sync trash: {error}'** + String failedToSyncTrash(String error); + + /// No description provided for @pleaseSelectAtLeastOneCollection. + /// + /// In en, this message translates to: + /// **'Please select at least one collection'** + String get pleaseSelectAtLeastOneCollection; + + /// No description provided for @unknownItemType. + /// + /// In en, this message translates to: + /// **'Unknown item type'** + String get unknownItemType; + + /// No description provided for @fileDeletedSuccessfully. + /// + /// In en, this message translates to: + /// **'File deleted successfully'** + String get fileDeletedSuccessfully; + + /// Error message when file deletion fails + /// + /// In en, this message translates to: + /// **'Failed to delete file: {error}'** + String failedToDeleteFile(String error); + + /// No description provided for @fileUpdatedSuccessfully. + /// + /// In en, this message translates to: + /// **'File updated successfully!'** + String get fileUpdatedSuccessfully; + + /// Error message when file update fails + /// + /// In en, this message translates to: + /// **'Failed to update file: {error}'** + String failedToUpdateFile(String error); + + /// No description provided for @noChangesWereMade. + /// + /// In en, this message translates to: + /// **'No changes were made'** + String get noChangesWereMade; + + /// No description provided for @noCollectionsAvailableForRestore. + /// + /// In en, this message translates to: + /// **'No collections available for restore'** + String get noCollectionsAvailableForRestore; + + /// No description provided for @trashClearedSuccessfully. + /// + /// In en, this message translates to: + /// **'Trash cleared successfully'** + String get trashClearedSuccessfully; + + /// Error message when clearing trash fails + /// + /// In en, this message translates to: + /// **'Failed to clear trash: {error}'** + String failedToClearTrash(String error); + + /// No description provided for @trash. + /// + /// In en, this message translates to: + /// **'Trash'** + String get trash; + + /// Message when file is permanently deleted from trash + /// + /// In en, this message translates to: + /// **'Deleted \"{fileName}\" permanently'** + String deletedPermanently(String fileName); + + /// Error message when file restoration fails + /// + /// In en, this message translates to: + /// **'Failed to restore \"{fileName}\": {error}'** + String failedToRestoreFile(String fileName, String error); + + /// No description provided for @createNewCollection. + /// + /// In en, this message translates to: + /// **'Create new collection'** + String get createNewCollection; + + /// No description provided for @create. + /// + /// In en, this message translates to: + /// **'Create'** + String get create; + + /// No description provided for @renameCollection. + /// + /// In en, this message translates to: + /// **'Rename collection'** + String get renameCollection; + + /// No description provided for @save. + /// + /// In en, this message translates to: + /// **'Save'** + String get save; + + /// No description provided for @deleteFile. + /// + /// In en, this message translates to: + /// **'Delete File'** + String get deleteFile; + + /// Confirmation message for file deletion + /// + /// In en, this message translates to: + /// **'Are you sure you want to delete \"{fileName}\"?'** + String deleteFileConfirmation(String fileName); + + /// No description provided for @noCollectionsFound. + /// + /// In en, this message translates to: + /// **'No collections found'** + String get noCollectionsFound; + + /// No description provided for @createYourFirstCollection. + /// + /// In en, this message translates to: + /// **'Create your first collection to get started'** + String get createYourFirstCollection; + + /// No description provided for @createCollection. + /// + /// In en, this message translates to: + /// **'Create Collection'** + String get createCollection; + + /// No description provided for @nothingYet. + /// + /// In en, this message translates to: + /// **'Nothing yet'** + String get nothingYet; + + /// No description provided for @uploadYourFirstDocument. + /// + /// In en, this message translates to: + /// **'Upload your first document to get started'** + String get uploadYourFirstDocument; + + /// No description provided for @uploadDocument. + /// + /// In en, this message translates to: + /// **'Upload Document'** + String get uploadDocument; + + /// Number of items in a collection + /// + /// In en, this message translates to: + /// **'{count} items'** + String items(int count); + + /// Number of files in a collection + /// + /// In en, this message translates to: + /// **'{count, plural, =0{no files} =1{1 file} other{{count} files}}'** + String files(int count); + + /// No description provided for @createCollectionTooltip. + /// + /// In en, this message translates to: + /// **'Create collection'** + String get createCollectionTooltip; + + /// No description provided for @uploadDocumentTooltip. + /// + /// In en, this message translates to: + /// **'Upload document'** + String get uploadDocumentTooltip; + + /// No description provided for @restore. + /// + /// In en, this message translates to: + /// **'Restore'** + String get restore; + + /// Dialog title for restoring a file from trash + /// + /// In en, this message translates to: + /// **'Restore {fileName}'** + String restoreFile(String fileName); + + /// No description provided for @noCollectionsAvailable. + /// + /// In en, this message translates to: + /// **'No collections available'** + String get noCollectionsAvailable; + + /// No description provided for @emptyTrash. + /// + /// In en, this message translates to: + /// **'Empty trash'** + String get emptyTrash; + + /// No description provided for @emptyTrashTooltip. + /// + /// In en, this message translates to: + /// **'Empty trash'** + String get emptyTrashTooltip; + + /// No description provided for @emptyTrashConfirmation. + /// + /// In en, this message translates to: + /// **'Are you sure you want to permanently delete all items in trash? This action cannot be undone.'** + String get emptyTrashConfirmation; + + /// No description provided for @fileTitle. + /// + /// In en, this message translates to: + /// **'File title'** + String get fileTitle; + + /// No description provided for @note. + /// + /// In en, this message translates to: + /// **'Note'** + String get note; + + /// No description provided for @optionalNote. + /// + /// In en, this message translates to: + /// **'Optional note'** + String get optionalNote; + + /// No description provided for @upload. + /// + /// In en, this message translates to: + /// **'Upload'** + String get upload; + + /// No description provided for @documentsHint. + /// + /// In en, this message translates to: + /// **'Documents'** + String get documentsHint; + + /// No description provided for @searchHint. + /// + /// In en, this message translates to: + /// **'Search...'** + String get searchHint; + + /// Message when no collections match the search query + /// + /// In en, this message translates to: + /// **'No collections found for \"{query}\"'** + String noCollectionsFoundForQuery(String query); + + /// No description provided for @uncategorized. + /// + /// In en, this message translates to: + /// **'Uncategorized'** + String get uncategorized; + + /// No description provided for @syncingTrash. + /// + /// In en, this message translates to: + /// **'Syncing trash...'** + String get syncingTrash; + + /// No description provided for @restoring. + /// + /// In en, this message translates to: + /// **'Restoring...'** + String get restoring; + + /// Success message when file is restored to a collection + /// + /// In en, this message translates to: + /// **'Restored \"{fileName}\" to \"{collectionName}\"'** + String restoredFileToCollection(String fileName, String collectionName); + + /// No description provided for @clearingTrash. + /// + /// In en, this message translates to: + /// **'Clearing trash...'** + String get clearingTrash; + + /// No description provided for @trashIsEmpty. + /// + /// In en, this message translates to: + /// **'Trash is empty'** + String get trashIsEmpty; + + /// No description provided for @share. + /// + /// In en, this message translates to: + /// **'Share'** + String get share; + + /// No description provided for @shareLink. + /// + /// In en, this message translates to: + /// **'Share link'** + String get shareLink; + + /// No description provided for @creatingShareLink. + /// + /// In en, this message translates to: + /// **'Creating link...'** + String get creatingShareLink; + + /// No description provided for @shareThisLink. + /// + /// In en, this message translates to: + /// **'Anyone with this link can access your file.'** + String get shareThisLink; + + /// No description provided for @copyLink. + /// + /// In en, this message translates to: + /// **'Copy link'** + String get copyLink; + + /// No description provided for @deleteLink. + /// + /// In en, this message translates to: + /// **'Delete link'** + String get deleteLink; + + /// No description provided for @close. + /// + /// In en, this message translates to: + /// **'Close'** + String get close; + + /// No description provided for @linkCopiedToClipboard. + /// + /// In en, this message translates to: + /// **'Link copied to clipboard'** + String get linkCopiedToClipboard; + + /// No description provided for @deleteShareLink. + /// + /// In en, this message translates to: + /// **'Delete link'** + String get deleteShareLink; + + /// No description provided for @deleteShareLinkDialogTitle. + /// + /// In en, this message translates to: + /// **'Delete link?'** + String get deleteShareLinkDialogTitle; + + /// No description provided for @deleteShareLinkConfirmation. + /// + /// In en, this message translates to: + /// **'People with this link will no longer be able to access the file.'** + String get deleteShareLinkConfirmation; + + /// No description provided for @deletingShareLink. + /// + /// In en, this message translates to: + /// **'Deleting link...'** + String get deletingShareLink; + + /// No description provided for @shareLinkDeletedSuccessfully. + /// + /// In en, this message translates to: + /// **'Link deleted successfully'** + String get shareLinkDeletedSuccessfully; + + /// No description provided for @failedToCreateShareLink. + /// + /// In en, this message translates to: + /// **'Failed to create link'** + String get failedToCreateShareLink; + + /// No description provided for @failedToDeleteShareLink. + /// + /// In en, this message translates to: + /// **'Failed to delete link'** + String get failedToDeleteShareLink; + + /// No description provided for @deletingFile. + /// + /// In en, this message translates to: + /// **'Deleting file...'** + String get deletingFile; + + /// No description provided for @addInformation. + /// + /// In en, this message translates to: + /// **'Add information'** + String get addInformation; + + /// No description provided for @addInformationDialogSubtitle. + /// + /// In en, this message translates to: + /// **'Choose the type of information you want to add'** + String get addInformationDialogSubtitle; + + /// No description provided for @physicalDocument. + /// + /// In en, this message translates to: + /// **'Physical document'** + String get physicalDocument; + + /// No description provided for @physicalDocumentDescription. + /// + /// In en, this message translates to: + /// **'Save information about documents and items in the real world.'** + String get physicalDocumentDescription; + + /// No description provided for @emergencyContact. + /// + /// In en, this message translates to: + /// **'Emergency contact'** + String get emergencyContact; + + /// No description provided for @emergencyContactDescription. + /// + /// In en, this message translates to: + /// **'Save information about important contacts.'** + String get emergencyContactDescription; + + /// No description provided for @accountCredential. + /// + /// In en, this message translates to: + /// **'Account credential'** + String get accountCredential; + + /// No description provided for @accountCredentialDescription. + /// + /// In en, this message translates to: + /// **'Save information about your important account credentials.'** + String get accountCredentialDescription; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en'].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return AppLocalizationsEn(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); +} diff --git a/mobile/apps/locker/lib/l10n/app_localizations_en.dart b/mobile/apps/locker/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000000..5fd46a81d3 --- /dev/null +++ b/mobile/apps/locker/lib/l10n/app_localizations_en.dart @@ -0,0 +1,410 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get onBoardingBody => 'Securely backup your documents'; + + @override + String get newUser => 'New to Ente'; + + @override + String get existingUser => 'Existing User'; + + @override + String get developerSettings => 'Developer settings'; + + @override + String get yes => 'Yes'; + + @override + String get developerSettingsWarning => + 'Are you sure that you want to modify Developer settings?'; + + @override + String get serverEndpoint => 'Server endpoint'; + + @override + String get endpointUpdatedMessage => 'Endpoint updated successfully'; + + @override + String get invalidEndpoint => 'Invalid endpoint'; + + @override + String get invalidEndpointMessage => + 'Sorry, the endpoint you entered is invalid. Please enter a valid endpoint and try again.'; + + @override + String get saveAction => 'Save'; + + @override + String get untitled => 'Untitled'; + + @override + String get edit => 'Edit'; + + @override + String get delete => 'Delete'; + + @override + String get noFilesFound => 'No files here'; + + @override + String get addFiles => 'Add Files'; + + @override + String get name => 'Name'; + + @override + String get date => 'Date'; + + @override + String get pleaseWait => 'Please wait...'; + + @override + String get downloading => 'Downloading...'; + + @override + String downloadingProgress(int percentage) { + return 'Downloading... $percentage%'; + } + + @override + String get downloadFailed => 'Download Failed'; + + @override + String get failedToDownloadOrDecrypt => 'Failed to download or decrypt file'; + + @override + String get errorOpeningFile => 'Error opening file'; + + @override + String errorOpeningFileMessage(String error) { + return 'Error opening file: $error'; + } + + @override + String couldNotOpenFile(String error) { + return 'Could not open file: $error'; + } + + @override + String minutesAgo(int minutes) { + return '${minutes}m ago'; + } + + @override + String hoursAgo(int hours) { + return '${hours}h ago'; + } + + @override + String get yesterday => 'Yesterday'; + + @override + String daysAgo(int days) { + return '${days}d ago'; + } + + @override + String get collectionRenamedSuccessfully => 'Collection renamed successfully'; + + @override + String failedToRenameCollection(String error) { + return 'Failed to rename collection: $error'; + } + + @override + String get collectionCannotBeDeleted => 'This collection cannot be deleted'; + + @override + String get deleteCollection => 'Delete collection'; + + @override + String deleteCollectionConfirmation(String collectionName) { + return 'Are you sure you want to delete \"$collectionName\"?'; + } + + @override + String get cancel => 'Cancel'; + + @override + String get collectionDeletedSuccessfully => 'Collection deleted successfully'; + + @override + String failedToDeleteCollection(String error) { + return 'Failed to delete collection: $error'; + } + + @override + String get collections => 'Collections'; + + @override + String get retry => 'Retry'; + + @override + String failedToSyncTrash(String error) { + return 'Failed to sync trash: $error'; + } + + @override + String get pleaseSelectAtLeastOneCollection => + 'Please select at least one collection'; + + @override + String get unknownItemType => 'Unknown item type'; + + @override + String get fileDeletedSuccessfully => 'File deleted successfully'; + + @override + String failedToDeleteFile(String error) { + return 'Failed to delete file: $error'; + } + + @override + String get fileUpdatedSuccessfully => 'File updated successfully!'; + + @override + String failedToUpdateFile(String error) { + return 'Failed to update file: $error'; + } + + @override + String get noChangesWereMade => 'No changes were made'; + + @override + String get noCollectionsAvailableForRestore => + 'No collections available for restore'; + + @override + String get trashClearedSuccessfully => 'Trash cleared successfully'; + + @override + String failedToClearTrash(String error) { + return 'Failed to clear trash: $error'; + } + + @override + String get trash => 'Trash'; + + @override + String deletedPermanently(String fileName) { + return 'Deleted \"$fileName\" permanently'; + } + + @override + String failedToRestoreFile(String fileName, String error) { + return 'Failed to restore \"$fileName\": $error'; + } + + @override + String get createNewCollection => 'Create new collection'; + + @override + String get create => 'Create'; + + @override + String get renameCollection => 'Rename collection'; + + @override + String get save => 'Save'; + + @override + String get deleteFile => 'Delete File'; + + @override + String deleteFileConfirmation(String fileName) { + return 'Are you sure you want to delete \"$fileName\"?'; + } + + @override + String get noCollectionsFound => 'No collections found'; + + @override + String get createYourFirstCollection => + 'Create your first collection to get started'; + + @override + String get createCollection => 'Create Collection'; + + @override + String get nothingYet => 'Nothing yet'; + + @override + String get uploadYourFirstDocument => + 'Upload your first document to get started'; + + @override + String get uploadDocument => 'Upload Document'; + + @override + String items(int count) { + return '$count items'; + } + + @override + String files(int count) { + String _temp0 = intl.Intl.pluralLogic( + count, + locale: localeName, + other: '$count files', + one: '1 file', + zero: 'no files', + ); + return '$_temp0'; + } + + @override + String get createCollectionTooltip => 'Create collection'; + + @override + String get uploadDocumentTooltip => 'Upload document'; + + @override + String get restore => 'Restore'; + + @override + String restoreFile(String fileName) { + return 'Restore $fileName'; + } + + @override + String get noCollectionsAvailable => 'No collections available'; + + @override + String get emptyTrash => 'Empty trash'; + + @override + String get emptyTrashTooltip => 'Empty trash'; + + @override + String get emptyTrashConfirmation => + 'Are you sure you want to permanently delete all items in trash? This action cannot be undone.'; + + @override + String get fileTitle => 'File title'; + + @override + String get note => 'Note'; + + @override + String get optionalNote => 'Optional note'; + + @override + String get upload => 'Upload'; + + @override + String get documentsHint => 'Documents'; + + @override + String get searchHint => 'Search...'; + + @override + String noCollectionsFoundForQuery(String query) { + return 'No collections found for \"$query\"'; + } + + @override + String get uncategorized => 'Uncategorized'; + + @override + String get syncingTrash => 'Syncing trash...'; + + @override + String get restoring => 'Restoring...'; + + @override + String restoredFileToCollection(String fileName, String collectionName) { + return 'Restored \"$fileName\" to \"$collectionName\"'; + } + + @override + String get clearingTrash => 'Clearing trash...'; + + @override + String get trashIsEmpty => 'Trash is empty'; + + @override + String get share => 'Share'; + + @override + String get shareLink => 'Share link'; + + @override + String get creatingShareLink => 'Creating link...'; + + @override + String get shareThisLink => 'Anyone with this link can access your file.'; + + @override + String get copyLink => 'Copy link'; + + @override + String get deleteLink => 'Delete link'; + + @override + String get close => 'Close'; + + @override + String get linkCopiedToClipboard => 'Link copied to clipboard'; + + @override + String get deleteShareLink => 'Delete link'; + + @override + String get deleteShareLinkDialogTitle => 'Delete link?'; + + @override + String get deleteShareLinkConfirmation => + 'People with this link will no longer be able to access the file.'; + + @override + String get deletingShareLink => 'Deleting link...'; + + @override + String get shareLinkDeletedSuccessfully => 'Link deleted successfully'; + + @override + String get failedToCreateShareLink => 'Failed to create link'; + + @override + String get failedToDeleteShareLink => 'Failed to delete link'; + + @override + String get deletingFile => 'Deleting file...'; + + @override + String get addInformation => 'Add information'; + + @override + String get addInformationDialogSubtitle => + 'Choose the type of information you want to add'; + + @override + String get physicalDocument => 'Physical document'; + + @override + String get physicalDocumentDescription => + 'Save information about documents and items in the real world.'; + + @override + String get emergencyContact => 'Emergency contact'; + + @override + String get emergencyContactDescription => + 'Save information about important contacts.'; + + @override + String get accountCredential => 'Account credential'; + + @override + String get accountCredentialDescription => + 'Save information about your important account credentials.'; +} diff --git a/mobile/apps/locker/lib/l10n/l10n.dart b/mobile/apps/locker/lib/l10n/l10n.dart new file mode 100644 index 0000000000..64aa328de9 --- /dev/null +++ b/mobile/apps/locker/lib/l10n/l10n.dart @@ -0,0 +1,8 @@ +import "package:flutter/widgets.dart"; +import 'package:locker/l10n/app_localizations.dart'; + +export "app_localizations.dart"; + +extension AppLocalizationsX on BuildContext { + AppLocalizations get l10n => AppLocalizations.of(this)!; +} diff --git a/mobile/apps/locker/lib/main.dart b/mobile/apps/locker/lib/main.dart new file mode 100644 index 0000000000..b0e7d68eb9 --- /dev/null +++ b/mobile/apps/locker/lib/main.dart @@ -0,0 +1,163 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:adaptive_theme/adaptive_theme.dart'; +import 'package:ente_accounts/services/user_service.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_lock_screen/lock_screen_settings.dart'; +import 'package:ente_lock_screen/ui/app_lock.dart'; +import 'package:ente_lock_screen/ui/lock_screen.dart'; +import 'package:ente_logging/logging.dart'; +import 'package:ente_network/network.dart'; +import 'package:ente_ui/utils/window_listener_service.dart'; +import 'package:ente_utils/platform_util.dart'; +import "package:flutter/material.dart"; +import 'package:flutter_displaymode/flutter_displaymode.dart'; +import 'package:locker/app.dart'; +import 'package:locker/core/locale.dart'; +import 'package:locker/l10n/app_localizations.dart'; +import 'package:locker/services/collections/collections_api_client.dart'; +import "package:locker/services/collections/collections_db.dart"; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/services/files/download/service_locator.dart'; +import "package:locker/services/files/links/links_client.dart"; +import "package:locker/services/files/links/links_service.dart"; +import "package:locker/services/trash/trash_db.dart"; +import 'package:locker/services/trash/trash_service.dart'; +import 'package:locker/ui/pages/home_page.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:tray_manager/tray_manager.dart'; +import 'package:window_manager/window_manager.dart'; + +final _logger = Logger("main"); + +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + + if (PlatformUtil.isDesktop()) { + await windowManager.ensureInitialized(); + await WindowListenerService.instance.init(); + final WindowOptions windowOptions = WindowOptions( + size: WindowListenerService.instance.getWindowSize(), + maximumSize: const Size(8192, 8192), + ); + await windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + _initSystemTray().ignore(); + }); + } + + await _runInForeground(); + if (Platform.isAndroid) { + FlutterDisplayMode.setHighRefreshRate().ignore(); + } +} + +Future _initSystemTray() async { + if (PlatformUtil.isMobile()) return; + final String path = Platform.isWindows + ? 'assets/icons/locker-icon.ico' + : 'assets/icons/locker-icon.png'; + await trayManager.setIcon(path); + final Menu menu = Menu( + items: [ + MenuItem( + key: 'hide_window', + label: 'Hide Window', + ), + MenuItem( + key: 'show_window', + label: 'Show Window', + ), + MenuItem.separator(), + MenuItem( + key: 'exit_app', + label: 'Exit App', + ), + ], + ); + await trayManager.setContextMenu(menu); +} + +Future _runInForeground() async { + final savedThemeMode = _themeMode(await AdaptiveTheme.getThemeMode()); + return await _runWithLogs(() async { + _logger.info("Starting app in foreground"); + try { + await _init(false, via: 'mainMethod'); + } catch (e, s) { + _logger.severe("Failed to init", e, s); + rethrow; + } + final Locale? locale = await getLocale(noFallback: true); + runApp( + AppLock( + builder: (args) => App(locale: locale), + lockScreen: LockScreen(Configuration.instance), + enabled: await LockScreenSettings.instance.shouldShowLockScreen(), + locale: locale, + savedThemeMode: savedThemeMode, + supportedLocales: appSupportedLocales, + localizationsDelegates: AppLocalizations.localizationsDelegates, + localeListResolutionCallback: localResolutionCallBack, + ), + ); + }); +} + +ThemeMode _themeMode(AdaptiveThemeMode? savedThemeMode) { + if (savedThemeMode == null) return ThemeMode.system; + if (savedThemeMode.isLight) return ThemeMode.light; + if (savedThemeMode.isDark) return ThemeMode.dark; + return ThemeMode.system; +} + +Future _runWithLogs(Function() function, {String prefix = ""}) async { + String dir = ""; + try { + dir = "${(await getApplicationSupportDirectory()).path}/logs"; + } catch (_) {} + await SuperLogging.main( + LogConfig( + body: function, + logDirPath: dir, + maxLogFiles: 5, + enableInDebugMode: true, + prefix: prefix, + ), + ); +} + +Future _init(bool bool, {String? via}) async { + final SharedPreferences preferences = await SharedPreferences.getInstance(); + final PackageInfo packageInfo = await PackageInfo.fromPlatform(); + + await CryptoUtil.init(); + + await CollectionDB.instance.init(); + await TrashDB.instance.init(); + + await Configuration.instance.init([ + CollectionDB.instance, + TrashDB.instance, + ]); + + await Network.instance.init(Configuration.instance); + await UserService.instance.init(Configuration.instance, const HomePage()); + await LockScreenSettings.instance.init(Configuration.instance); + await CollectionApiClient.instance.init(); + await CollectionService.instance.init(); + await LinksClient.instance.init(); + await LinksService.instance.init(); + await ServiceLocator.instance.init( + preferences, + Network.instance.enteDio, + Network.instance.getDio(), + packageInfo, + ); + await TrashService.instance.init(preferences); +} diff --git a/mobile/apps/locker/lib/services/collections/collections_api_client.dart b/mobile/apps/locker/lib/services/collections/collections_api_client.dart new file mode 100644 index 0000000000..bb2c2003a4 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/collections_api_client.dart @@ -0,0 +1,452 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_network/network.dart'; +import 'package:locker/core/errors.dart'; +import "package:locker/services/collections/collections_service.dart"; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/collections/models/collection_file_item.dart'; +import 'package:locker/services/collections/models/collection_magic.dart'; +import 'package:locker/services/collections/models/diff.dart'; +import 'package:locker/services/configuration.dart'; +import "package:locker/services/files/sync/metadata_updater_service.dart"; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/files/sync/models/file_magic.dart'; +import 'package:locker/services/trash/models/trash_item_request.dart'; +import "package:locker/utils/crypto_helper.dart"; +import 'package:logging/logging.dart'; + +class CollectionApiClient { + CollectionApiClient._privateConstructor(); + + static final CollectionApiClient instance = + CollectionApiClient._privateConstructor(); + + final _logger = Logger("CollectionApiClient"); + final _enteDio = Network.instance.enteDio; + final _config = Configuration.instance; + + Future init() async {} + + Future> getCollections(int sinceTime) async { + try { + final response = await _enteDio.get( + "/collections/v2", + queryParameters: { + "sinceTime": sinceTime, + }, + ); + final List collections = []; + final c = response.data["collections"]; + for (final collectionData in c) { + final Collection collection = + await _fromRemoteCollection(collectionData); + collections.add(collection); + } + return collections; + } catch (e, s) { + _logger.warning(e, s); + if (e is DioException && e.response?.statusCode == 401) { + throw UnauthorizedError(); + } + rethrow; + } + } + + Future getFiles(Collection collection, int sinceTime) async { + _logger.info( + "[Collection-${collection.id}] fetch diff since: $sinceTime", + ); + bool hasMore = true; + final List updatedFiles = []; + final List deletedFiles = []; + int latestUpdatedAtTime = 0; + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + while (hasMore) { + final diff = await _getDiff(collection.id, collectionKey, sinceTime); + updatedFiles.addAll(diff.updatedFiles); + deletedFiles.addAll(diff.deletedFiles); + latestUpdatedAtTime = max(latestUpdatedAtTime, diff.latestUpdatedAtTime); + hasMore = diff.hasMore; + } + final finalDiff = Diff( + updatedFiles, + deletedFiles, + false, + latestUpdatedAtTime, + ); + _logger.info("[Collection-${collection.id}] fetched"); + return finalDiff; + } + + Future addToCollection( + Collection collection, + List files, + ) async { + final pendingUpload = files.any( + (element) => element.uploadedFileID == null, + ); + if (pendingUpload) { + throw ArgumentError('Can only add uploaded files'); + } + if (files.isEmpty) { + _logger.info("nothing to add to the collection"); + return; + } + final params = {}; + params["collectionID"] = collection.id; + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + params["files"] = []; + for (final file in files) { + final int uploadedFileID = file.uploadedFileID!; + final fileKey = await CollectionService.instance.getFileKey(file); + final encryptedKeyData = CryptoUtil.encryptSync(fileKey, collectionKey); + final String encryptedKey = + CryptoUtil.bin2base64(encryptedKeyData.encryptedData!); + final String keyDecryptionNonce = + CryptoUtil.bin2base64(encryptedKeyData.nonce!); + params["files"].add( + CollectionFileItem(uploadedFileID, encryptedKey, keyDecryptionNonce) + .toMap(), + ); + } + try { + await _enteDio.post( + "/collections/add-files", + data: params, + ); + } catch (e) { + _logger.warning('failed to add files to collection', e); + rethrow; + } + } + + Future trash(List requests) async { + final requestData = {}; + requestData["items"] = []; + for (final request in requests) { + requestData["items"].add(request.toJson()); + } + final response = await _enteDio.post( + "/files/trash", + data: requestData, + ); + if (response.statusCode != 200) { + throw Exception("Failed to remove files from collection"); + } + } + + Future rename(Collection collection, String newName) async { + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + final encryptedName = CryptoUtil.encryptSync( + utf8.encode(newName), + collectionKey, + ); + final params = {}; + params["collectionID"] = collection.id; + params["encryptedName"] = + CryptoUtil.bin2base64(encryptedName.encryptedData!); + params["nameDecryptionNonce"] = CryptoUtil.bin2base64(encryptedName.nonce!); + try { + await _enteDio.post( + "/collections/rename", + data: params, + ); + } catch (e) { + _logger.warning("failed to rename collection", e); + rethrow; + } + } + + Future move( + EnteFile file, + Collection fromCollection, + Collection toCollection, + ) async { + final params = {}; + params["fromCollectionID"] = fromCollection.id; + params["toCollectionID"] = toCollection.id; + final fileKey = await CollectionService.instance.getFileKey(file); + final encryptedKeyData = CryptoUtil.encryptSync( + fileKey, + CryptoHelper.instance.getCollectionKey(toCollection), + ); + params["files"] = []; + params["files"].add( + CollectionFileItem( + file.uploadedFileID!, + CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), + CryptoUtil.bin2base64(encryptedKeyData.nonce!), + ).toMap(), + ); + await _enteDio.post( + "/collections/move-files", + data: params, + ); + } + + Future trashCollection(Collection collection) async { + try { + await _enteDio.delete( + "/collections/v3/${collection.id}?keepFiles=False&collectionID=${collection.id}", + ); + } catch (e) { + _logger.severe('failed to trash collection', e); + rethrow; + } + } + + Future _getDiff( + int collectionID, + Uint8List collectionKey, + int sinceTime, + ) async { + try { + final response = await _enteDio.get( + "/collections/v2/diff", + queryParameters: { + "collectionID": collectionID, + "sinceTime": sinceTime, + }, + ); + int latestUpdatedAtTime = 0; + final diff = response.data["diff"] as List; + final bool hasMore = response.data["hasMore"] as bool; + final startTime = DateTime.now(); + final deletedFiles = []; + final updatedFiles = []; + + for (final item in diff) { + final file = EnteFile(); + file.uploadedFileID = item["id"]; + file.collectionID = item["collectionID"]; + file.updationTime = item["updationTime"]; + latestUpdatedAtTime = max(latestUpdatedAtTime, file.updationTime!); + if (item["isDeleted"]) { + deletedFiles.add(file); + continue; + } + file.ownerID = item["ownerID"]; + file.fileDecryptionHeader = item["file"]["decryptionHeader"]; + file.thumbnailDecryptionHeader = item["thumbnail"]["decryptionHeader"]; + file.metadataDecryptionHeader = item["metadata"]["decryptionHeader"]; + if (item["info"] != null) { + file.fileSize = item["info"]["fileSize"]; + } + file.encryptedKey = item["encryptedKey"]; + file.keyDecryptionNonce = item["keyDecryptionNonce"]; + final fileKey = CryptoHelper.instance.getFileKey( + file.encryptedKey!, + file.keyDecryptionNonce!, + collectionKey, + ); + final encodedMetadata = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item["metadata"]["encryptedData"]), + fileKey, + CryptoUtil.base642bin(file.metadataDecryptionHeader!), + ); + final Map metadata = + jsonDecode(utf8.decode(encodedMetadata)); + file.applyMetadata(metadata); + if (item['magicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item['magicMetadata']['data']), + fileKey, + CryptoUtil.base642bin(item['magicMetadata']['header']), + ); + file.mMdEncodedJson = utf8.decode(utfEncodedMmd); + file.mMdVersion = item['magicMetadata']['version']; + file.magicMetadata = + MagicMetadata.fromEncodedJson(file.mMdEncodedJson!); + } + if (item['pubMagicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item['pubMagicMetadata']['data']), + fileKey, + CryptoUtil.base642bin(item['pubMagicMetadata']['header']), + ); + file.pubMmdEncodedJson = utf8.decode(utfEncodedMmd); + file.pubMmdVersion = item['pubMagicMetadata']['version']; + file.pubMagicMetadata = + PubMagicMetadata.fromEncodedJson(file.pubMmdEncodedJson!); + } + updatedFiles.add(file); + } + _logger.info('[Collection-$collectionID] parsed ${diff.length} ' + 'diff items ( ${updatedFiles.length} updated) in ${DateTime.now().difference(startTime).inMilliseconds}ms'); + return Diff(updatedFiles, deletedFiles, hasMore, latestUpdatedAtTime); + } catch (e, s) { + _logger.severe(e, s); + rethrow; + } + } + + Future _fromRemoteCollection( + Map? collectionData, + ) async { + final Collection collection = Collection.fromMap(collectionData); + if (collectionData != null && !collection.isDeleted) { + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + if (collectionData['magicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(collectionData['magicMetadata']['data']), + collectionKey, + CryptoUtil.base642bin(collectionData['magicMetadata']['header']), + ); + collection.mMdEncodedJson = utf8.decode(utfEncodedMmd); + collection.mMdVersion = collectionData['magicMetadata']['version']; + collection.magicMetadata = CollectionMagicMetadata.fromEncodedJson( + collection.mMdEncodedJson ?? '{}', + ); + } + + if (collectionData['pubMagicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(collectionData['pubMagicMetadata']['data']), + collectionKey, + CryptoUtil.base642bin( + collectionData['pubMagicMetadata']['header'], + ), + ); + collection.mMdPubEncodedJson = utf8.decode(utfEncodedMmd); + collection.mMbPubVersion = + collectionData['pubMagicMetadata']['version']; + collection.pubMagicMetadata = + CollectionPubMagicMetadata.fromEncodedJson( + collection.mMdPubEncodedJson ?? '{}', + ); + } + if (collectionData['sharedMagicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin( + collectionData['sharedMagicMetadata']['data'], + ), + collectionKey, + CryptoUtil.base642bin( + collectionData['sharedMagicMetadata']['header'], + ), + ); + collection.sharedMmdJson = utf8.decode(utfEncodedMmd); + collection.sharedMmdVersion = + collectionData['sharedMagicMetadata']['version']; + collection.sharedMagicMetadata = ShareeMagicMetadata.fromEncodedJson( + collection.sharedMmdJson ?? '{}', + ); + } + } + collection.setName(_getDecryptedCollectionName(collection)); + return collection; + } + + String _getDecryptedCollectionName(Collection collection) { + if (collection.isDeleted) { + return "Deleted Collection"; + } + if (collection.encryptedName != null && + collection.encryptedName!.isNotEmpty) { + try { + final collectionKey = + CryptoHelper.instance.getCollectionKey(collection); + final result = CryptoUtil.decryptSync( + CryptoUtil.base642bin(collection.encryptedName!), + collectionKey, + CryptoUtil.base642bin(collection.nameDecryptionNonce!), + ); + return utf8.decode(result); + } catch (e, s) { + _logger.severe( + "failed to decrypt collection name: ${collection.id}", + e, + s, + ); + } + } + return collection.name ?? "Untitled"; + } + + Future create(String name, CollectionType type) async { + final collectionKey = CryptoUtil.generateKey(); + final encryptedKeyData = + CryptoUtil.encryptSync(collectionKey, _config.getKey()!); + final encryptedName = CryptoUtil.encryptSync( + utf8.encode(name), + collectionKey, + ); + final request = CreateRequest( + encryptedKey: CryptoUtil.bin2base64(encryptedKeyData.encryptedData!), + keyDecryptionNonce: CryptoUtil.bin2base64(encryptedKeyData.nonce!), + encryptedName: CryptoUtil.bin2base64(encryptedName.encryptedData!), + nameDecryptionNonce: CryptoUtil.bin2base64(encryptedName.nonce!), + type: type, + attributes: CollectionAttributes(), + ); + return _enteDio + .post( + "/collections", + data: request.toJson(), + ) + .then((response) async { + final collectionData = response.data["collection"]; + final collection = await _fromRemoteCollection(collectionData); + return collection; + }); + } +} + +class CreateRequest { + String encryptedKey; + String keyDecryptionNonce; + String encryptedName; + String nameDecryptionNonce; + CollectionType type; + CollectionAttributes? attributes; + MetadataRequest? magicMetadata; + + CreateRequest({ + required this.encryptedKey, + required this.keyDecryptionNonce, + required this.encryptedName, + required this.nameDecryptionNonce, + required this.type, + this.attributes, + this.magicMetadata, + }); + + CreateRequest copyWith({ + String? encryptedKey, + String? keyDecryptionNonce, + String? encryptedName, + String? nameDecryptionNonce, + CollectionType? type, + CollectionAttributes? attributes, + MetadataRequest? magicMetadata, + }) => + CreateRequest( + encryptedKey: encryptedKey ?? this.encryptedKey, + keyDecryptionNonce: keyDecryptionNonce ?? this.keyDecryptionNonce, + encryptedName: encryptedName ?? this.encryptedName, + nameDecryptionNonce: nameDecryptionNonce ?? this.nameDecryptionNonce, + type: type ?? this.type, + attributes: attributes ?? this.attributes, + magicMetadata: magicMetadata ?? this.magicMetadata, + ); + + Map toJson() { + final map = {}; + map['encryptedKey'] = encryptedKey; + map['keyDecryptionNonce'] = keyDecryptionNonce; + map['encryptedName'] = encryptedName; + map['nameDecryptionNonce'] = nameDecryptionNonce; + map['type'] = typeToString(type); + if (attributes != null) { + map['attributes'] = attributes!.toMap(); + } + if (magicMetadata != null) { + map['magicMetadata'] = magicMetadata!.toJson(); + } + return map; + } +} diff --git a/mobile/apps/locker/lib/services/collections/collections_db.dart b/mobile/apps/locker/lib/services/collections/collections_db.dart new file mode 100644 index 0000000000..6a1b22a6c9 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/collections_db.dart @@ -0,0 +1,535 @@ +import 'dart:convert'; + +import "package:ente_base/models/database.dart"; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/collections/models/public_url.dart'; +import 'package:locker/services/collections/models/user.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqflite/sqflite.dart'; + +class CollectionDB extends EnteBaseDatabase { + CollectionDB._privateConstructor(); + + static final CollectionDB instance = CollectionDB._privateConstructor(); + + Database? _database; + int _collectionSyncTime = 0; + final Map _collectionSyncTimesCache = {}; + + static const String _collectionsTable = 'collections'; + static const String _filesTable = 'files'; + static const String _collectionFilesTable = 'collection_files'; + static const String _syncTimesTable = 'sync_times'; + + Future init() async { + _database = await _initDatabase(); + await _loadCaches(); + } + + Future _loadCaches() async { + final syncTimes = await _db.query( + _syncTimesTable, + where: 'key LIKE ?', + whereArgs: ['collection_sync_time_%'], + ); + + for (final row in syncTimes) { + final key = row['key'] as String; + final collectionId = + int.tryParse(key.replaceFirst('collection_sync_time_', '')); + if (collectionId != null) { + _collectionSyncTimesCache[collectionId] = row['value'] as int; + } + } + + final globalSyncTime = await getSyncTimeAsync(); + _collectionSyncTime = globalSyncTime; + } + + Future _initDatabase() async { + final documentsDirectory = await getApplicationDocumentsDirectory(); + final path = join(documentsDirectory.path, 'collection_store.db'); + + return await openDatabase( + path, + version: 1, + onCreate: _createTables, + ); + } + + Future _createTables(Database db, int version) async { + await db.execute(''' + CREATE TABLE $_collectionsTable ( + id INTEGER PRIMARY KEY, + owner_id INTEGER, + owner_email TEXT, + owner_name TEXT, + encrypted_key TEXT, + key_decryption_nonce TEXT, + name TEXT, + type TEXT, + attributes_version INTEGER, + attributes_encrypted_path TEXT, + attributes_path_decryption_nonce TEXT, + sharees TEXT, + public_urls TEXT, + updation_time INTEGER, + is_deleted INTEGER DEFAULT 0, + decrypted_path TEXT, + m_md_encoded_json TEXT, + m_md_pub_encoded_json TEXT, + shared_mmd_json TEXT, + m_md_version INTEGER DEFAULT 0, + m_mb_pub_version INTEGER DEFAULT 0, + shared_mmd_version INTEGER DEFAULT 0 + ) + '''); + + await db.execute(''' + CREATE TABLE $_filesTable ( + uploaded_file_id INTEGER, + local_path TEXT, + owner_id INTEGER, + collection_id INTEGER, + title TEXT, + creation_time INTEGER, + modification_time INTEGER, + updation_time INTEGER, + added_time INTEGER, + hash TEXT, + metadata_version INTEGER, + encrypted_key TEXT, + key_decryption_nonce TEXT, + file_decryption_header TEXT, + thumbnail_decryption_header TEXT, + metadata_decryption_header TEXT, + file_size INTEGER, + m_md_encoded_json TEXT, + m_md_version INTEGER DEFAULT 0, + pub_mmd_encoded_json TEXT, + pub_mmd_version INTEGER DEFAULT 1 + ) + '''); + + await db.execute(''' + CREATE TABLE $_collectionFilesTable ( + collection_id INTEGER, + uploaded_file_id INTEGER, + PRIMARY KEY (collection_id, uploaded_file_id) + ) + '''); + + await db.execute(''' + CREATE TABLE $_syncTimesTable ( + key TEXT PRIMARY KEY, + value INTEGER NOT NULL + ) + '''); + + await db.execute(''' + CREATE INDEX idx_files_collection_id ON $_filesTable (collection_id) + '''); + + await db.execute(''' + CREATE INDEX idx_files_uploaded_file_id ON $_filesTable (uploaded_file_id) + '''); + + await db.execute(''' + CREATE INDEX idx_collection_files_uploaded_file_id ON $_collectionFilesTable (uploaded_file_id) + '''); + + await db.execute(''' + CREATE INDEX idx_collections_updation_time ON $_collectionsTable (updation_time) + '''); + } + + Database get _db { + if (_database == null) { + throw Exception('Database not initialized. Call init() first.'); + } + return _database!; + } + + Future setSyncTime(int lastSyncTime) async { + _collectionSyncTime = lastSyncTime; + await _db.insert( + _syncTimesTable, + {'key': 'collection_sync_time', 'value': lastSyncTime}, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + int getSyncTime() { + return _collectionSyncTime; + } + + Future getSyncTimeAsync() async { + final result = await _db.query( + _syncTimesTable, + where: 'key = ?', + whereArgs: ['collection_sync_time'], + ); + + if (result.isNotEmpty) { + _collectionSyncTime = result.first['value'] as int; + return _collectionSyncTime; + } + return 0; + } + + int getCollectionSyncTime(int collectionId) { + return _collectionSyncTimesCache[collectionId] ?? 0; + } + + Future getCollectionSyncTimeAsync(int collectionId) async { + final result = await _db.query( + _syncTimesTable, + where: 'key = ?', + whereArgs: ['collection_sync_time_$collectionId'], + ); + + int syncTime = 0; + if (result.isNotEmpty) { + syncTime = result.first['value'] as int; + } + + _collectionSyncTimesCache[collectionId] = syncTime; + return syncTime; + } + + Future setCollectionSyncTime(int collectionId, int lastSyncTime) async { + await _db.insert( + _syncTimesTable, + {'key': 'collection_sync_time_$collectionId', 'value': lastSyncTime}, + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + _collectionSyncTimesCache[collectionId] = lastSyncTime; + } + + Future getCollection(int id) async { + final result = await _db.query( + _collectionsTable, + where: 'id = ?', + whereArgs: [id], + ); + + if (result.isEmpty) { + throw Exception('Collection with id $id not found'); + } + + return _mapToCollection(result.first); + } + + Future updateCollections(List collections) async { + final batch = _db.batch(); + + for (final collection in collections) { + batch.delete( + _collectionsTable, + where: 'id = ?', + whereArgs: [collection.id], + ); + + batch.insert( + _collectionsTable, + _collectionToMap(collection), + ); + } + + await batch.commit(); + } + + Future deleteCollection(Collection collection) async { + final batch = _db.batch(); + + batch.delete( + _collectionsTable, + where: 'id = ?', + whereArgs: [collection.id], + ); + + batch.delete( + _collectionFilesTable, + where: 'collection_id = ?', + whereArgs: [collection.id], + ); + + final filesInCollection = await _db.query( + _collectionFilesTable, + where: 'collection_id = ?', + whereArgs: [collection.id], + ); + + for (final fileMap in filesInCollection) { + final uploadedFileId = fileMap['uploaded_file_id'] as int; + final otherCollections = await _db.query( + _collectionFilesTable, + where: 'uploaded_file_id = ? AND collection_id != ?', + whereArgs: [uploadedFileId, collection.id], + ); + + if (otherCollections.isEmpty) { + batch.delete( + _filesTable, + where: 'uploaded_file_id = ?', + whereArgs: [uploadedFileId], + ); + } + } + + await batch.commit(); + } + + Future> getCollections() async { + final result = await _db.query(_collectionsTable); + return result.map((row) => _mapToCollection(row)).toList(); + } + + Future addFilesToCollection( + Collection collection, + List files, + ) async { + final batch = _db.batch(); + + for (final file in files) { + batch.insert( + _filesTable, + _fileToMap(file), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + + batch.insert( + _collectionFilesTable, + { + 'collection_id': collection.id, + 'uploaded_file_id': file.uploadedFileID!, + }, + conflictAlgorithm: ConflictAlgorithm.ignore, + ); + } + + await batch.commit(); + } + + Future deleteFilesFromCollection( + Collection collection, + List files, + ) async { + final batch = _db.batch(); + + for (final file in files) { + batch.delete( + _collectionFilesTable, + where: 'collection_id = ? AND uploaded_file_id = ?', + whereArgs: [collection.id, file.uploadedFileID!], + ); + + final otherCollections = await _db.query( + _collectionFilesTable, + where: 'uploaded_file_id = ? AND collection_id != ?', + whereArgs: [file.uploadedFileID!, collection.id], + ); + + if (otherCollections.isEmpty) { + batch.delete( + _filesTable, + where: 'uploaded_file_id = ?', + whereArgs: [file.uploadedFileID!], + ); + } + } + + await batch.commit(); + } + + Future> getFilesInCollection(Collection collection) async { + final result = await _db.rawQuery( + ''' + SELECT f.* + FROM $_filesTable f + INNER JOIN $_collectionFilesTable cf ON f.uploaded_file_id = cf.uploaded_file_id + WHERE cf.collection_id = ? + ''', + [collection.id], + ); + + return result.map((row) => _mapToFile(row)).toList(); + } + + Future> getCollectionsForFile(EnteFile file) async { + final result = await _db.rawQuery( + ''' + SELECT c.* + FROM $_collectionsTable c + INNER JOIN $_collectionFilesTable cf ON c.id = cf.collection_id + WHERE cf.uploaded_file_id = ? + ''', + [file.uploadedFileID!], + ); + + return result.map((row) => _mapToCollection(row)).toList(); + } + + Future> getAllFiles() async { + final result = await _db.query(_filesTable); + return result.map((row) => _mapToFile(row)).toList(); + } + + Map _collectionToMap(Collection collection) { + return { + 'id': collection.id, + 'owner_id': collection.owner.id, + 'owner_email': collection.owner.email, + 'owner_name': collection.owner.name, + 'encrypted_key': collection.encryptedKey, + 'key_decryption_nonce': collection.keyDecryptionNonce, + 'name': collection.name, + 'type': typeToString(collection.type), + 'attributes_version': collection.attributes.version, + 'attributes_encrypted_path': collection.attributes.encryptedPath, + 'attributes_path_decryption_nonce': + collection.attributes.pathDecryptionNonce, + 'sharees': + jsonEncode(collection.sharees.map((user) => user.toMap()).toList()), + 'public_urls': + jsonEncode(collection.publicURLs.map((url) => url.toMap()).toList()), + 'updation_time': collection.updationTime, + 'is_deleted': collection.isDeleted ? 1 : 0, + 'decrypted_path': collection.decryptedPath, + 'm_md_encoded_json': collection.mMdEncodedJson, + 'm_md_pub_encoded_json': collection.mMdPubEncodedJson, + 'shared_mmd_json': collection.sharedMmdJson, + 'm_md_version': collection.mMdVersion, + 'm_mb_pub_version': collection.mMbPubVersion, + 'shared_mmd_version': collection.sharedMmdVersion, + }; + } + + Collection _mapToCollection(Map map) { + final owner = User( + id: map['owner_id'] as int?, + email: map['owner_email'] as String, + name: map['owner_name'] as String?, + ); + + final attributes = CollectionAttributes( + version: map['attributes_version'] as int? ?? 0, + encryptedPath: map['attributes_encrypted_path'] as String?, + pathDecryptionNonce: map['attributes_path_decryption_nonce'] as String?, + ); + + final shareesJson = map['sharees'] as String? ?? '[]'; + final shareesData = jsonDecode(shareesJson) as List; + final sharees = shareesData + .map((shareeeMap) => User.fromMap(shareeeMap as Map)) + .where((user) => user != null) + .cast() + .toList(); + + final publicUrlsJson = map['public_urls'] as String? ?? '[]'; + final publicURLsData = jsonDecode(publicUrlsJson) as List; + final publicURLs = publicURLsData + .map((urlMap) => PublicURL.fromMap(urlMap as Map)) + .where((url) => url != null) + .cast() + .toList(); + + final collection = Collection( + map['id'] as int, + owner, + map['encrypted_key'] as String, + map['key_decryption_nonce'] as String?, + map['name'] as String?, + null, + null, + typeFromString(map['type'] as String), + attributes, + sharees, + publicURLs, + map['updation_time'] as int, + isDeleted: (map['is_deleted'] as int? ?? 0) == 1, + ); + + collection.decryptedPath = map['decrypted_path'] as String?; + collection.mMdEncodedJson = map['m_md_encoded_json'] as String?; + collection.mMdPubEncodedJson = map['m_md_pub_encoded_json'] as String?; + collection.sharedMmdJson = map['shared_mmd_json'] as String?; + collection.mMdVersion = map['m_md_version'] as int? ?? 0; + collection.mMbPubVersion = map['m_mb_pub_version'] as int? ?? 0; + collection.sharedMmdVersion = map['shared_mmd_version'] as int? ?? 0; + + return collection; + } + + Map _fileToMap(EnteFile file) { + return { + 'uploaded_file_id': file.uploadedFileID, + 'local_path': file.localPath, + 'owner_id': file.ownerID, + 'collection_id': file.collectionID, + 'title': file.title, + 'creation_time': file.creationTime, + 'modification_time': file.modificationTime, + 'updation_time': file.updationTime, + 'added_time': file.addedTime, + 'hash': file.hash, + 'metadata_version': file.metadataVersion, + 'encrypted_key': file.encryptedKey, + 'key_decryption_nonce': file.keyDecryptionNonce, + 'file_decryption_header': file.fileDecryptionHeader, + 'thumbnail_decryption_header': file.thumbnailDecryptionHeader, + 'metadata_decryption_header': file.metadataDecryptionHeader, + 'file_size': file.fileSize, + 'm_md_encoded_json': file.mMdEncodedJson, + 'm_md_version': file.mMdVersion, + 'pub_mmd_encoded_json': file.pubMmdEncodedJson, + 'pub_mmd_version': file.pubMmdVersion, + }; + } + + EnteFile _mapToFile(Map map) { + final file = EnteFile(); + + file.localPath = map['local_path']; + file.uploadedFileID = map['uploaded_file_id']; + file.ownerID = map['owner_id']; + file.collectionID = map['collection_id']; + file.title = map['title']; + file.creationTime = map['creation_time']; + file.modificationTime = map['modification_time']; + file.updationTime = map['updation_time']; + file.addedTime = map['added_time']; + file.hash = map['hash']; + file.metadataVersion = map['metadata_version']; + file.encryptedKey = map['encrypted_key']; + file.keyDecryptionNonce = map['key_decryption_nonce']; + file.fileDecryptionHeader = map['file_decryption_header']; + file.thumbnailDecryptionHeader = map['thumbnail_decryption_header']; + file.metadataDecryptionHeader = map['metadata_decryption_header']; + file.fileSize = map['file_size']; + file.mMdEncodedJson = map['m_md_encoded_json']; + file.mMdVersion = map['m_md_version'] ?? 0; + file.pubMmdEncodedJson = map['pub_mmd_encoded_json']; + file.pubMmdVersion = map['pub_mmd_version'] ?? 1; + + return file; + } + + Future close() async { + await _database?.close(); + _database = null; + } + + @override + Future clearTable() async { + await _database?.delete(_collectionsTable); + await _database?.delete(_filesTable); + await _database?.delete(_collectionFilesTable); + await _database?.delete(_syncTimesTable); + _collectionSyncTimesCache.clear(); + _collectionSyncTime = 0; + } +} diff --git a/mobile/apps/locker/lib/services/collections/collections_service.dart b/mobile/apps/locker/lib/services/collections/collections_service.dart new file mode 100644 index 0000000000..9813e68e49 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/collections_service.dart @@ -0,0 +1,343 @@ +import 'dart:async'; +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:ente_events/event_bus.dart'; +import 'package:ente_events/models/signed_in_event.dart'; +import 'package:locker/events/collections_updated_event.dart'; +import "package:locker/services/collections/collections_api_client.dart"; +import "package:locker/services/collections/collections_db.dart"; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/trash/models/trash_item_request.dart'; +import "package:locker/services/trash/trash_service.dart"; +import "package:locker/utils/crypto_helper.dart"; +import 'package:logging/logging.dart'; + +class CollectionService { + CollectionService._privateConstructor(); + + static final CollectionService instance = + CollectionService._privateConstructor(); + + // Fixed set of suggested collection names + static const Set _suggestedCollectionNames = { + 'Personal', + 'Work', + 'Travel', + 'Family', + 'Projects', + 'School', + 'Music', + 'Books', + 'Events', + 'Holidays', + }; + + final _logger = Logger("CollectionService"); + final _apiClient = CollectionApiClient.instance; + + Future init() async { + if (Configuration.instance.hasConfiguredAccount()) { + await _init(); + } else { + Bus.instance.on().listen((event) { + _logger.info("User signed in, starting initial sync."); + _init(); + }); + } + } + + Future sync() async { + final updatedCollections = await CollectionApiClient.instance + .getCollections(CollectionDB.instance.getSyncTime()); + if (updatedCollections.isEmpty) { + _logger.info("No collections to sync."); + return; + } + await CollectionDB.instance.updateCollections(updatedCollections); + await CollectionDB.instance + .setSyncTime(updatedCollections.last.updationTime); + final List fileFutures = []; + for (final collection in updatedCollections) { + if (collection.isDeleted) { + await CollectionDB.instance.deleteCollection(collection); + continue; + } + final syncTime = + CollectionDB.instance.getCollectionSyncTime(collection.id); + fileFutures.add( + CollectionApiClient.instance + .getFiles(collection, syncTime) + .then((diff) async { + if (diff.updatedFiles.isNotEmpty) { + await CollectionDB.instance.addFilesToCollection( + collection, + diff.updatedFiles, + ); + } + if (diff.deletedFiles.isNotEmpty) { + await CollectionDB.instance.deleteFilesFromCollection( + collection, + diff.deletedFiles, + ); + } + await CollectionDB.instance + .setCollectionSyncTime(collection.id, diff.latestUpdatedAtTime); + }).catchError((e) { + _logger.warning( + "Failed to fetch files for collection ${collection.id}: $e", + ); + }), + ); + } + await Future.wait(fileFutures); + if (updatedCollections.isNotEmpty) { + Bus.instance.fire(CollectionsUpdatedEvent()); + } + } + + bool hasCompletedFirstSync() { + return Configuration.instance.hasConfiguredAccount() && + CollectionDB.instance.getSyncTime() > 0; + } + + Future createCollection( + String name, { + CollectionType type = CollectionType.folder, + }) async { + try { + final collection = await _apiClient.create(name, type); + _logger.info("Created collection: ${collection.name}"); + // Let sync update the local state + await sync(); + return collection; + } catch (e) { + _logger.severe("Failed to create collection: $e"); + rethrow; + } + } + + Future> getCollections() async { + return CollectionDB.instance.getCollections(); + } + + Future> getCollectionsForFile(EnteFile file) async { + return CollectionDB.instance.getCollectionsForFile(file); + } + + Future> getFilesInCollection(Collection collection) async { + try { + final files = + await CollectionDB.instance.getFilesInCollection(collection); + return files; + } catch (e) { + _logger.severe( + "Failed to fetch files for collection ${collection.name}: $e", + ); + rethrow; + } + } + + Future> getAllFiles() async { + try { + final allFiles = await CollectionDB.instance.getAllFiles(); + return allFiles; + } catch (e) { + _logger.severe("Failed to fetch all files: $e"); + rethrow; + } + } + + Future addToCollection(Collection collection, EnteFile file) async { + try { + await _apiClient.addToCollection(collection, [file]); + _logger.info("Added file ${file.title} to collection ${collection.name}"); + // Let sync update the local state + await sync(); + } catch (e) { + _logger.severe("Failed to add file to collection: $e"); + rethrow; + } + } + + Future trashFile(EnteFile file, Collection collection) async { + try { + final List requests = []; + requests.add(TrashRequest(file.uploadedFileID!, collection.id)); + await _apiClient.trash(requests); + // Let sync update the local state + await sync(); + await TrashService.instance.syncTrash(); + } catch (e) { + _logger.severe("Failed to remove file from collections: $e"); + rethrow; + } + } + + Future rename(Collection collection, String newName) async { + try { + await CollectionApiClient.instance.rename( + collection, + newName, + ); + _logger.info("Renamed collection ${collection.name} to $newName"); + // Let sync update the local state + await sync(); + } catch (e, s) { + _logger.warning("failed to rename collection", e, s); + rethrow; + } + } + + Future getOrCreateUncategorizedCollection() async { + final collections = await getCollections(); + for (final collection in collections) { + if (collection.type == CollectionType.uncategorized) { + return collection; + } + } + _logger.info("No collections found, creating uncategorized collection."); + return await createCollection( + "Uncategorized", + type: CollectionType.uncategorized, + ); + } + + Future _init() async { + // ignore: unawaited_futures + sync().then((_) { + setupDefaultCollections(); + }).catchError((error) { + _logger.severe("Failed to initialize collections: $error"); + }); + } + + Future _getOrCreateImportantCollection() async { + final collections = await getCollections(); + for (final collection in collections) { + if (collection.type == CollectionType.favorites) { + return collection; + } + } + _logger.info("No collections found, creating important collection."); + return await createCollection("Important", type: CollectionType.favorites); + } + + Future move(EnteFile file, Collection from, Collection to) async { + try { + await _apiClient.move(file, from, to); + _logger.info("Moved file ${file.title} from ${from.name} to ${to.name}"); + // Let sync update the local state + await sync(); + } catch (e) { + _logger.severe("Failed to move file: $e"); + rethrow; + } + } + + Future trashCollection(Collection collection) async { + try { + await _apiClient.trashCollection(collection); + _logger.info("Trashed collection: ${collection.name}"); + // Let sync update the local state + await sync(); + } catch (e) { + _logger.severe("Failed to trash collection: $e"); + rethrow; + } + } + + Future setupDefaultCollections() async { + try { + _logger.info("Setting up default collections..."); + + // Create uncategorized collection if it doesn't exist + await getOrCreateUncategorizedCollection(); + + // Create important (favorites) collection if it doesn't exist + await _getOrCreateImportantCollection(); + + // Create Documents collection if it doesn't exist + await _getOrCreateDocumentsCollection(); + + _logger.info("Default collections setup completed."); + } catch (e, s) { + _logger.severe("Failed to setup default collections", e, s); + } + } + + Future _getOrCreateDocumentsCollection() async { + final collections = await getCollections(); + for (final collection in collections) { + if (collection.type == CollectionType.folder && + collection.name == "Documents") { + return collection; + } + } + _logger + .info("No Documents collection found, creating Documents collection."); + return createCollection("Documents", type: CollectionType.folder); + } + + /// Returns one random collection name that doesn't already exist + /// If all names are used, returns "Documents" + Future getRandomUnusedCollectionName() async { + try { + final existingCollections = await getCollections(); + final existingNames = existingCollections + .map((collection) => collection.name?.toLowerCase()) + .where((name) => name != null) + .toSet(); + + final availableNames = _suggestedCollectionNames + .where((name) => !existingNames.contains(name.toLowerCase())) + .toList(); + + if (availableNames.isEmpty) { + _logger.info( + "All suggested collection names are used, returning 'Documents'", + ); + return "Documents"; + } + + final random = Random(); + final randomName = availableNames[random.nextInt(availableNames.length)]; + _logger.info("Selected random unused collection name: $randomName"); + return randomName; + } catch (e) { + _logger.severe("Failed to get random unused collection name: $e"); + return "Documents"; + } + } + + Future getCollection(int collectionID) async { + return await CollectionDB.instance.getCollection(collectionID); + } + + Future getCollectionKey(int collectionID) async { + final collection = await getCollection(collectionID); + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + return collectionKey; + } + + Future getFileKey(EnteFile file) async { + try { + final collection = await getCollection(file.collectionID!); + final collectionKey = CryptoHelper.instance.getCollectionKey(collection); + + final fileKey = CryptoHelper.instance.getFileKey( + file.encryptedKey!, + file.keyDecryptionNonce!, + collectionKey, + ); + + _logger.info("Successfully decrypted file key for file ${file.title}"); + return fileKey; + } catch (e) { + _logger.severe("Failed to get file key: $e"); + rethrow; + } + } +} diff --git a/mobile/apps/locker/lib/services/collections/models/collection.dart b/mobile/apps/locker/lib/services/collections/models/collection.dart new file mode 100644 index 0000000000..2338bc11d9 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/collection.dart @@ -0,0 +1,352 @@ +import 'dart:core'; + +import 'package:flutter/foundation.dart'; +import 'package:locker/services/collections/models/collection_magic.dart'; +import 'package:locker/services/collections/models/public_url.dart'; +import 'package:locker/services/collections/models/user.dart'; +import 'package:locker/services/files/sync/models/common_keys.dart'; + +class Collection { + final int id; + final User owner; + final String encryptedKey; + final String? keyDecryptionNonce; + String? name; + + final String? encryptedName; + final String? nameDecryptionNonce; + final CollectionType type; + final CollectionAttributes attributes; + final List sharees; + final List publicURLs; + final int updationTime; + final bool isDeleted; + + // decryptedPath will be null for collections now owned by user, deleted + // collections, && collections which don't have a path. The path is used + // to map local on-device album on mobile to remote collection on ente. + String? decryptedPath; + String? mMdEncodedJson; + String? mMdPubEncodedJson; + String? sharedMmdJson; + int mMdVersion = 0; + int mMbPubVersion = 0; + int sharedMmdVersion = 0; + CollectionMagicMetadata? _mmd; + CollectionPubMagicMetadata? _pubMmd; + ShareeMagicMetadata? _sharedMmd; + + CollectionMagicMetadata get magicMetadata => + _mmd ?? CollectionMagicMetadata.fromEncodedJson(mMdEncodedJson ?? '{}'); + + CollectionPubMagicMetadata get pubMagicMetadata => + _pubMmd ?? + CollectionPubMagicMetadata.fromEncodedJson(mMdPubEncodedJson ?? '{}'); + + ShareeMagicMetadata get sharedMagicMetadata => + _sharedMmd ?? ShareeMagicMetadata.fromEncodedJson(sharedMmdJson ?? '{}'); + + set magicMetadata(CollectionMagicMetadata? val) => _mmd = val; + + set pubMagicMetadata(CollectionPubMagicMetadata? val) => _pubMmd = val; + + set sharedMagicMetadata(ShareeMagicMetadata? val) => _sharedMmd = val; + + void setName(String newName) { + name = newName; + } + + Collection( + this.id, + this.owner, + this.encryptedKey, + this.keyDecryptionNonce, + this.name, + this.encryptedName, + this.nameDecryptionNonce, + this.type, + this.attributes, + this.sharees, + this.publicURLs, + this.updationTime, { + this.isDeleted = false, + }); + + bool isArchived() { + return mMdVersion > 0 && magicMetadata.visibility == archiveVisibility; + } + + bool hasShareeArchived() { + return sharedMmdVersion > 0 && + sharedMagicMetadata.visibility == archiveVisibility; + } + + // hasLink returns true if there's any link attached to the collection + // including expired links + bool get hasLink => publicURLs.isNotEmpty; + + bool get hasCover => (pubMagicMetadata.coverID ?? 0) > 0; + + // hasSharees returns true if the collection is shared with other ente users + bool get hasSharees => sharees.isNotEmpty; + + bool get isPinned => (magicMetadata.order ?? 0) != 0; + + bool isHidden() { + if (isDefaultHidden()) { + return true; + } + return mMdVersion > 0 && (magicMetadata.visibility == hiddenVisibility); + } + + bool isDefaultHidden() { + return (magicMetadata.subType ?? 0) == subTypeDefaultHidden; + } + + bool isQuickLinkCollection() { + return (magicMetadata.subType ?? 0) == subTypeSharedFilesCollection && + !hasSharees; + } + + List getSharees() { + return sharees; + } + + bool isOwner(int userID) { + return (owner.id ?? -100) == userID; + } + + bool isDownloadEnabledForPublicLink() { + if (publicURLs.isEmpty) { + return false; + } + return publicURLs.first.enableDownload; + } + + bool isCollectEnabledForPublicLink() { + if (publicURLs.isEmpty) { + return false; + } + return publicURLs.first.enableCollect; + } + + bool get isJoinEnabled { + if (publicURLs.isEmpty) { + return false; + } + return publicURLs.first.enableJoin; + } + + CollectionParticipantRole getRole(int userID) { + if (isOwner(userID)) { + return CollectionParticipantRole.owner; + } + if (sharees.isEmpty) { + return CollectionParticipantRole.unknown; + } + for (final User u in sharees) { + if (u.id == userID) { + if (u.isViewer) { + return CollectionParticipantRole.viewer; + } else if (u.isCollaborator) { + return CollectionParticipantRole.collaborator; + } + } + } + return CollectionParticipantRole.unknown; + } + + void updateSharees(List newSharees) { + sharees.clear(); + sharees.addAll(newSharees); + } + + Collection copyWith({ + int? id, + User? owner, + String? encryptedKey, + String? keyDecryptionNonce, + String? name, + String? encryptedName, + String? nameDecryptionNonce, + CollectionType? type, + CollectionAttributes? attributes, + List? sharees, + List? publicURLs, + int? updationTime, + bool? isDeleted, + String? mMdEncodedJson, + int? mMdVersion, + String? decryptedName, + String? decryptedPath, + }) { + final Collection result = Collection( + id ?? this.id, + owner ?? this.owner, + encryptedKey ?? this.encryptedKey, + keyDecryptionNonce ?? this.keyDecryptionNonce, + // ignore: deprecated_member_use_from_same_package + name ?? this.name, + encryptedName ?? this.encryptedName, + nameDecryptionNonce ?? this.nameDecryptionNonce, + type ?? this.type, + attributes ?? this.attributes, + sharees ?? this.sharees, + publicURLs ?? this.publicURLs, + updationTime ?? this.updationTime, + isDeleted: isDeleted ?? this.isDeleted, + ); + result.mMdVersion = mMdVersion ?? this.mMdVersion; + result.mMdEncodedJson = mMdEncodedJson ?? this.mMdEncodedJson; + result.decryptedPath = decryptedPath ?? this.decryptedPath; + result.mMbPubVersion = mMbPubVersion; + result.mMdPubEncodedJson = mMdPubEncodedJson; + result.sharedMmdVersion = sharedMmdVersion; + result.sharedMmdJson = sharedMmdJson; + return result; + } + + static fromMap(Map? map) { + if (map == null) return null; + final sharees = (map['sharees'] == null || map['sharees'].length == 0) + ? [] + : List.from(map['sharees'].map((x) => User.fromMap(x))); + final publicURLs = + (map['publicURLs'] == null || map['publicURLs'].length == 0) + ? [] + : List.from( + map['publicURLs'].map((x) => PublicURL.fromMap(x)), + ); + return Collection( + map['id'], + User.fromMap(map['owner']), + map['encryptedKey'], + map['keyDecryptionNonce'], + map['name'], + map['encryptedName'], + map['nameDecryptionNonce'], + typeFromString(map['type']), + CollectionAttributes.fromMap(map['attributes']), + sharees, + publicURLs, + map['updationTime'], + isDeleted: map['isDeleted'] ?? false, + ); + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is Collection && o.id == id; + } + + @override + int get hashCode { + return id.hashCode; + } +} + +enum CollectionType { + folder, + favorites, + uncategorized, + album, + unknown, +} + +CollectionType typeFromString(String type) { + switch (type) { + case "folder": + return CollectionType.folder; + case "favorites": + return CollectionType.favorites; + case "uncategorized": + return CollectionType.uncategorized; + case "album": + return CollectionType.album; + case "unknown": + return CollectionType.unknown; + } + debugPrint("unexpected collection type $type"); + return CollectionType.unknown; +} + +String typeToString(CollectionType type) { + switch (type) { + case CollectionType.folder: + return "folder"; + case CollectionType.favorites: + return "favorites"; + case CollectionType.album: + return "album"; + case CollectionType.uncategorized: + return "uncategorized"; + case CollectionType.unknown: + return "unknown"; + } +} + +extension CollectionTypeExtn on CollectionType { + bool get canDelete => + this != CollectionType.favorites && this != CollectionType.uncategorized; +} + +enum CollectionParticipantRole { + unknown, + viewer, + collaborator, + owner, +} + +extension CollectionParticipantRoleExtn on CollectionParticipantRole { + static CollectionParticipantRole fromString(String? val) { + if ((val ?? '') == '') { + return CollectionParticipantRole.viewer; + } + for (var x in CollectionParticipantRole.values) { + if (x.name.toUpperCase() == val!.toUpperCase()) { + return x; + } + } + return CollectionParticipantRole.unknown; + } + + String toStringVal() { + return name.toUpperCase(); + } +} + +class CollectionAttributes { + final String? encryptedPath; + final String? pathDecryptionNonce; + final int? version; + + CollectionAttributes({ + this.encryptedPath, + this.pathDecryptionNonce, + this.version, + }); + + Map toMap() { + final map = {}; + if (encryptedPath != null) { + map['encryptedPath'] = encryptedPath; + } + if (pathDecryptionNonce != null) { + map['pathDecryptionNonce'] = pathDecryptionNonce; + } + map['version'] = version ?? 0; + return map; + } + + static fromMap(Map? map) { + if (map == null) return null; + + return CollectionAttributes( + encryptedPath: map['encryptedPath'], + pathDecryptionNonce: map['pathDecryptionNonce'], + version: map['version'] ?? 0, + ); + } +} diff --git a/mobile/apps/locker/lib/services/collections/models/collection_file_item.dart b/mobile/apps/locker/lib/services/collections/models/collection_file_item.dart new file mode 100644 index 0000000000..514808cb9a --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/collection_file_item.dart @@ -0,0 +1,66 @@ +import 'dart:convert'; + +class CollectionFileItem { + final int id; + final String encryptedKey; + final String keyDecryptionNonce; + + CollectionFileItem( + this.id, + this.encryptedKey, + this.keyDecryptionNonce, + ); + + CollectionFileItem copyWith({ + int? id, + String? encryptedKey, + String? keyDecryptionNonce, + }) { + return CollectionFileItem( + id ?? this.id, + encryptedKey ?? this.encryptedKey, + keyDecryptionNonce ?? this.keyDecryptionNonce, + ); + } + + Map toMap() { + return { + 'id': id, + 'encryptedKey': encryptedKey, + 'keyDecryptionNonce': keyDecryptionNonce, + }; + } + + static fromMap(Map? map) { + if (map == null) return null; + + return CollectionFileItem( + map['id'], + map['encryptedKey'], + map['keyDecryptionNonce'], + ); + } + + String toJson() => json.encode(toMap()); + + factory CollectionFileItem.fromJson(String source) => + CollectionFileItem.fromMap(json.decode(source)); + + @override + String toString() => + 'CollectionFileItem(id: $id, encryptedKey: $encryptedKey, keyDecryptionNonce: $keyDecryptionNonce)'; + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is CollectionFileItem && + o.id == id && + o.encryptedKey == encryptedKey && + o.keyDecryptionNonce == keyDecryptionNonce; + } + + @override + int get hashCode => + id.hashCode ^ encryptedKey.hashCode ^ keyDecryptionNonce.hashCode; +} diff --git a/mobile/apps/locker/lib/services/collections/models/collection_magic.dart b/mobile/apps/locker/lib/services/collections/models/collection_magic.dart new file mode 100644 index 0000000000..8d3963a5ed --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/collection_magic.dart @@ -0,0 +1,126 @@ +import 'dart:convert'; + +import "package:locker/services/files/sync/models/common_keys.dart"; + +// Collection SubType Constants +const subTypeDefaultHidden = 1; +const subTypeSharedFilesCollection = 2; + +// key for collection subType +const subTypeKey = 'subType'; + +const muteKey = "mute"; + +const orderKey = "order"; + +class CollectionMagicMetadata { + // 0 -> visible + // 1 -> archived + // 2 -> hidden + int visibility; + + // null/0 value -> no subType + // 1 -> DEFAULT_HIDDEN COLLECTION for files hidden individually + // 2 -> Collections created for sharing selected files + int? subType; + + /* order is initially just used for pinned collections. + Later it can be used for custom sort order for if needed. + Higher the value, higher the preference of the collection to show up first. + */ + int? order; + + CollectionMagicMetadata({required this.visibility, this.subType, this.order}); + + Map toJson() { + final result = {magicKeyVisibility: visibility}; + if (subType != null) { + result[subTypeKey] = subType!; + } + if (order != null) { + result[orderKey] = order!; + } + return result; + } + + factory CollectionMagicMetadata.fromEncodedJson(String encodedJson) => + CollectionMagicMetadata.fromJson(jsonDecode(encodedJson)); + + factory CollectionMagicMetadata.fromJson(dynamic json) => + CollectionMagicMetadata.fromMap(json); + + static fromMap(Map? map) { + if (map == null) return null; + return CollectionMagicMetadata( + visibility: map[magicKeyVisibility] ?? visibleVisibility, + subType: map[subTypeKey], + order: map[orderKey], + ); + } +} + +class CollectionPubMagicMetadata { + // sort order while showing collection + bool? asc; + + // cover photo id for the collection + int? coverID; + + CollectionPubMagicMetadata({this.asc, this.coverID}); + + Map toJson() { + final Map result = {"asc": asc ?? false}; + if (coverID != null) { + result["coverID"] = coverID!; + } + return result; + } + + factory CollectionPubMagicMetadata.fromEncodedJson(String encodedJson) => + CollectionPubMagicMetadata.fromJson(jsonDecode(encodedJson)); + + factory CollectionPubMagicMetadata.fromJson(dynamic json) => + CollectionPubMagicMetadata.fromMap(json); + + static fromMap(Map? map) { + if (map == null) return null; + return CollectionPubMagicMetadata( + asc: map["asc"] as bool?, + coverID: map["coverID"], + ); + } +} + +class ShareeMagicMetadata { + // 0 -> visible + // 1 -> archived + // 2 -> hidden etc? + int visibility; + + // null/false value -> no mute + bool? mute; + + ShareeMagicMetadata({required this.visibility, this.mute}); + + Map toJson() { + final Map result = {magicKeyVisibility: visibility}; + if (mute != null) { + result[muteKey] = mute!; + } + return result; + } + + factory ShareeMagicMetadata.fromEncodedJson(String encodedJson) => + ShareeMagicMetadata.fromJson(jsonDecode(encodedJson)); + + factory ShareeMagicMetadata.fromJson(dynamic json) => + ShareeMagicMetadata.fromMap(json); + + static fromMap(Map? map) { + if (map == null) return null; + return ShareeMagicMetadata( + visibility: map[magicKeyVisibility] ?? visibleVisibility, + mute: map[muteKey], + ); + } +} diff --git a/mobile/apps/locker/lib/services/collections/models/diff.dart b/mobile/apps/locker/lib/services/collections/models/diff.dart new file mode 100644 index 0000000000..c37beaa4ad --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/diff.dart @@ -0,0 +1,15 @@ +import 'package:locker/services/files/sync/models/file.dart'; + +class Diff { + final List updatedFiles; + final List deletedFiles; + final bool hasMore; + final int latestUpdatedAtTime; + + Diff( + this.updatedFiles, + this.deletedFiles, + this.hasMore, + this.latestUpdatedAtTime, + ); +} diff --git a/mobile/apps/locker/lib/services/collections/models/public_url.dart b/mobile/apps/locker/lib/services/collections/models/public_url.dart new file mode 100644 index 0000000000..12556873a0 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/public_url.dart @@ -0,0 +1,63 @@ +class PublicURL { + String url; + int deviceLimit; + int validTill; + bool enableDownload; + bool enableCollect; + bool passwordEnabled; + bool enableJoin; + String? nonce; + int? opsLimit; + int? memLimit; + + PublicURL({ + required this.url, + required this.deviceLimit, + required this.validTill, + this.enableDownload = true, + this.passwordEnabled = false, + this.enableCollect = false, + this.enableJoin = false, + this.nonce, + this.opsLimit, + this.memLimit, + }); + + Map toMap() { + return { + 'url': url, + 'deviceLimit': deviceLimit, + 'validTill': validTill, + 'enableDownload': enableDownload, + 'passwordEnabled': passwordEnabled, + 'enableCollect': enableCollect, + 'nonce': nonce, + 'memLimit': memLimit, + 'opsLimit': opsLimit, + 'enableJoin': enableJoin, + }; + } + + bool get hasExpiry => validTill != 0; + + // isExpired indicates whether the link has expired or not + bool get isExpired => + hasExpiry && validTill < DateTime.now().microsecondsSinceEpoch; + + static fromMap(Map? map) { + if (map == null) return null; + + return PublicURL( + url: map['url'], + deviceLimit: map['deviceLimit'], + validTill: map['validTill'] ?? 0, + enableDownload: map['enableDownload'] ?? true, + passwordEnabled: map['passwordEnabled'] ?? false, + enableCollect: map['enableCollect'] ?? false, + nonce: map['nonce'], + opsLimit: map['opsLimit'], + memLimit: map['memLimit'], + enableJoin: map['enableJoin'] ?? false, + ); + } +} diff --git a/mobile/apps/locker/lib/services/collections/models/user.dart b/mobile/apps/locker/lib/services/collections/models/user.dart new file mode 100644 index 0000000000..30785b0504 --- /dev/null +++ b/mobile/apps/locker/lib/services/collections/models/user.dart @@ -0,0 +1,44 @@ +import "dart:convert"; + +class User { + int? id; + String email; + @Deprecated( + "Use displayName() extension method instead. Note: Some early users have" + " value in name field.", + ) + String? name; + String? role; + + User({ + this.id, + required this.email, + this.name, + this.role, + }); + + bool get isViewer => role == null || role?.toUpperCase() == 'VIEWER'; + + bool get isCollaborator => + role != null && role?.toUpperCase() == 'COLLABORATOR'; + + Map toMap() { + // ignore: deprecated_member_use_from_same_package + return {'id': id, 'email': email, 'name': name, "role": role}; + } + + static fromMap(Map? map) { + if (map == null) return null; + + return User( + id: map['id'], + email: map['email'], + name: map['name'], + role: map['role'] ?? 'VIEWER', + ); + } + + String toJson() => json.encode(toMap()); + + factory User.fromJson(String source) => User.fromMap(json.decode(source)); +} diff --git a/mobile/apps/locker/lib/services/configuration.dart b/mobile/apps/locker/lib/services/configuration.dart new file mode 100644 index 0000000000..f1a0b6d97d --- /dev/null +++ b/mobile/apps/locker/lib/services/configuration.dart @@ -0,0 +1,6 @@ +import 'package:ente_configuration/base_configuration.dart'; + +class Configuration extends BaseConfiguration { + Configuration._privateConstructor(); + static final Configuration instance = Configuration._privateConstructor(); +} diff --git a/mobile/apps/locker/lib/services/files/download/file_downloader.dart b/mobile/apps/locker/lib/services/files/download/file_downloader.dart new file mode 100644 index 0000000000..77acd82ef3 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/download/file_downloader.dart @@ -0,0 +1,115 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:dio/dio.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_network/network.dart'; +import 'package:ente_utils/fake_progress.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/services/files/download/models/task.dart'; +import 'package:locker/services/files/download/service_locator.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/utils/data_util.dart'; +import 'package:logging/logging.dart'; + +final _logger = Logger("FileDownloader"); + +Future downloadAndDecrypt( + EnteFile file, + Uint8List fileKey, { + ProgressCallback? progressCallback, + bool shouldFakeProgress = false, + bool shouldUseCache = false, +}) async { + final String logPrefix = 'File-${file.uploadedFileID}:'; + _logger.info('$logPrefix starting download'); + + final String tempDir = Configuration.instance.getTempDirectory(); + final String cacheDir = Configuration.instance.getCacheDirectory(); + + String encryptedFilePath = "$tempDir${file.uploadedFileID}.encrypted"; + File encryptedFile = File(encryptedFilePath); + + final String decryptedFilePath = shouldUseCache + ? "$cacheDir${file.displayName}" + : "$tempDir${file.displayName}"; + + final startTime = DateTime.now().millisecondsSinceEpoch; + + try { + if (downloadManager.enableResumableDownload(file.fileSize)) { + final DownloadResult result = await downloadManager.download( + file.uploadedFileID!, + file.displayName, + file.fileSize!, + ); + if (result.success) { + encryptedFilePath = result.task.filePath!; + encryptedFile = File(encryptedFilePath); + } else { + _logger.warning( + '$logPrefix download failed ${result.task.error} ${result.task.status}', + ); + return null; + } + } else { + // If the file is small, download it directly to the final location + final response = await Network.instance.getDio().download( + file.downloadUrl, + encryptedFilePath, + options: Options( + headers: {"X-Auth-Token": Configuration.instance.getToken()}, + ), + onReceiveProgress: (a, b) { + progressCallback?.call(a, b); + }, + ); + if (response.statusCode != 200 || !encryptedFile.existsSync()) { + _logger.warning('$logPrefix download failed ${response.toString()}'); + return null; + } + } + + final int sizeInBytes = file.fileSize ?? await encryptedFile.length(); + final double elapsedSeconds = + (DateTime.now().millisecondsSinceEpoch - startTime) / 1000; + final double speedInKBps = sizeInBytes / 1024.0 / elapsedSeconds; + + _logger.info( + '$logPrefix download completed: ${formatBytes(sizeInBytes)}, avg speed: ${speedInKBps.toStringAsFixed(2)} KB/s', + ); + + // As decryption can take time, emit fake progress for large files during + // decryption + final FakePeriodicProgress? fakeProgress = shouldFakeProgress + ? FakePeriodicProgress( + callback: (count) { + progressCallback?.call(sizeInBytes, sizeInBytes); + }, + duration: const Duration(milliseconds: 5000), + ) + : null; + try { + // Start the periodic callback after initial 5 seconds + fakeProgress?.start(); + await CryptoUtil.decryptFile( + encryptedFilePath, + decryptedFilePath, + CryptoUtil.base642bin(file.fileDecryptionHeader!), + fileKey, + ); + fakeProgress?.stop(); + _logger + .info('$logPrefix decryption completed (ID ${file.uploadedFileID})'); + } catch (e, s) { + fakeProgress?.stop(); + _logger.severe("Critical: $logPrefix failed to decrypt", e, s); + return null; + } + await encryptedFile.delete(); + return File(decryptedFilePath); + } catch (e, s) { + _logger.severe("$logPrefix failed to download or decrypt", e, s); + return null; + } +} diff --git a/mobile/apps/locker/lib/services/files/download/file_url.dart b/mobile/apps/locker/lib/services/files/download/file_url.dart new file mode 100644 index 0000000000..ec93dd4867 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/download/file_url.dart @@ -0,0 +1,41 @@ +import "package:locker/core/constants.dart"; +import "package:locker/services/configuration.dart"; + +enum FileUrlType { + download, + publicDownload, + thumbnail, + publicThumbnail, + directDownload, +} + +class FileUrl { + static String getUrl(int fileID, FileUrlType type) { + final endpoint = Configuration.instance.getHttpEndpoint(); + final disableWorker = endpoint != kDefaultProductionEndpoint; + + switch (type) { + case FileUrlType.directDownload: + return "$endpoint/files/download/$fileID"; + case FileUrlType.download: + return disableWorker + ? "$endpoint/files/download/$fileID" + : "https://files.ente.io/?fileID=$fileID"; + + case FileUrlType.publicDownload: + return disableWorker + ? "$endpoint/public-collection/files/download/$fileID" + : "https://public-albums.ente.io/download/?fileID=$fileID"; + + case FileUrlType.thumbnail: + return disableWorker + ? "$endpoint/files/preview/$fileID" + : "https://thumbnails.ente.io/?fileID=$fileID"; + + case FileUrlType.publicThumbnail: + return disableWorker + ? "$endpoint/public-collection/files/preview/$fileID" + : "https://public-albums.ente.io/preview/?fileID=$fileID"; + } + } +} diff --git a/mobile/apps/locker/lib/services/files/download/manager.dart b/mobile/apps/locker/lib/services/files/download/manager.dart new file mode 100644 index 0000000000..2685f2e053 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/download/manager.dart @@ -0,0 +1,373 @@ +import "dart:async"; +import "dart:io"; + +import "package:dio/dio.dart"; +import "package:locker/services/configuration.dart"; +import "package:locker/services/files/download/file_url.dart"; +import "package:locker/services/files/download/models/task.dart"; +import "package:logging/logging.dart"; + +class DownloadManager { + final _logger = Logger('DownloadManager'); + static const int downloadChunkSize = 40 * 1024 * 1024; + + final Dio _dio; + + // In-memory storage for download tasks + final Map _tasks = {}; + + // Active downloads with their completers and streams + final Map> _completers = {}; + final Map> _streams = {}; + final Map _cancelTokens = {}; + + DownloadManager(this._dio); + + /// Subscribe to download progress updates for a specific file ID + Stream watchDownload(int fileId) { + _streams[fileId] ??= StreamController.broadcast(); + return _streams[fileId]!.stream; + } + + bool enableResumableDownload(int? size) { + if (size == null) return false; + //todo: Use FileUrlType.direct instead of FileUrlType.directDownload + return size > downloadChunkSize; + } + + /// Start download and return a Future that completes when download finishes + /// If download was paused, calling this again will resume it + Future download( + int fileId, + String filename, + int totalBytes, + ) async { + // If already downloading, return existing future + if (_completers.containsKey(fileId)) { + return _completers[fileId]!.future; + } + + final completer = Completer(); + _completers[fileId] = completer; + + // Get or create task + final existingTask = _tasks[fileId]; + final task = existingTask ?? + DownloadTask( + id: fileId, + filename: filename, + totalBytes: totalBytes, + ); + + // Store task in memory + _tasks[fileId] = task; + + // Don't restart if already completed + if (task.isCompleted) { + // ensure that the file exists + final filePath = task.filePath; + if (filePath == null || !(await File(filePath).exists())) { + // If the file doesn't exist, mark the task as error + _logger.warning( + 'File not found for ${task.filename} (${task.bytesDownloaded}/${task.totalBytes} bytes)', + ); + final updatedTask = task.copyWith( + status: DownloadStatus.error, + error: 'File not found', + filePath: null, + ); + _updateTask(updatedTask); + final result = DownloadResult(updatedTask, false); + completer.complete(result); + return result; + } else { + _logger.info( + 'Download already completed for ${task.filename} (${task.bytesDownloaded}/${task.totalBytes} bytes)', + ); + final result = DownloadResult(task, true); + completer.complete(result); + return result; + } + } + unawaited(_startDownload(task, completer)); + return completer.future; + } + + /// Pause download + Future pause(int fileId) async { + final token = _cancelTokens[fileId]; + if (token != null && !token.isCancelled) { + token.cancel('paused'); + } + + final task = _tasks[fileId]; + if (task != null && task.isActive) { + _updateTask(task.copyWith(status: DownloadStatus.paused)); + } + + // Clean up streams if no listeners + final stream = _streams[fileId]; + if (stream != null && !stream.hasListener) { + await stream.close(); + _streams.remove(fileId); + } + } + + /// Cancel and delete download + Future cancel(int fileId) async { + final token = _cancelTokens[fileId]; + if (token != null && !token.isCancelled) { + token.cancel('cancelled'); + } + + final task = _tasks[fileId]; + if (task != null) { + await _deleteFiles(task); + _updateTask(task.copyWith(status: DownloadStatus.cancelled)); + _tasks.remove(fileId); + } + _cleanup(fileId); + } + + /// Get current download status + Future getDownload(int fileId) async => _tasks[fileId]; + + /// Get all downloads + Future> getAllDownloads() async => _tasks.values.toList(); + + Future _startDownload( + DownloadTask task, + Completer completer, + ) async { + try { + task = task.copyWith(status: DownloadStatus.downloading); + _updateTask(task); + + final cancelToken = CancelToken(); + _cancelTokens[task.id] = cancelToken; + + final directory = Configuration.instance.getTempDirectory(); + final basePath = '$directory${task.id}.encrypted'; + + // Check existing chunks and calculate progress + final totalChunks = (task.totalBytes / downloadChunkSize).ceil(); + final existingChunks = + await _validateExistingChunks(basePath, task.totalBytes, totalChunks); + + task = task.copyWith( + bytesDownloaded: _calculateDownloadedBytes( + existingChunks, + task.totalBytes, + totalChunks, + ), + ); + _updateTask(task); + + _logger.info( + 'Resuming download for ${task.filename} (${task.bytesDownloaded}/${task.totalBytes} bytes)', + ); + for (int i = 0; i < totalChunks; i++) { + if (existingChunks[i] || cancelToken.isCancelled) continue; + _logger.info('Downloading chunk ${i + 1} of $totalChunks'); + await _downloadChunk(task, basePath, i, totalChunks, cancelToken); + existingChunks[i] = true; + } + + if (!cancelToken.isCancelled) { + final finalPath = await _combineChunks(basePath, totalChunks); + task = task.copyWith( + status: DownloadStatus.completed, + filePath: finalPath, + bytesDownloaded: task.totalBytes, + ); + _updateTask(task); + completer.complete(DownloadResult(task, true)); + } + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) { + // Complete future with current task state (paused or cancelled) + final currentTask = _tasks[task.id]; + if (currentTask != null && !completer.isCompleted) { + completer.complete(DownloadResult(currentTask, false)); + } + return; + } + + task = task.copyWith(status: DownloadStatus.error, error: e.toString()); + _updateTask(task); + if (!completer.isCompleted) { + completer.complete(DownloadResult(task, false)); + } + } finally { + _cleanup(task.id); + } + } + + String _getChunkPath(String basePath, int part) { + return '$basePath.${part}_part'; + } + + Future> _validateExistingChunks( + String basePath, + int totalBytes, + int totalChunks, + ) async { + final existingChunks = List.filled(totalChunks, false); + + for (int i = 0; i < totalChunks; i++) { + final chunkFile = File(_getChunkPath(basePath, i + 1)); + if (!await chunkFile.exists()) continue; + + final expectedSize = i == totalChunks - 1 + ? totalBytes - (i * downloadChunkSize) + : downloadChunkSize; + + final actualSize = await chunkFile.length(); + if (actualSize == expectedSize) { + _logger.info('existing chunk ${i + 1} is valid'); + existingChunks[i] = true; + } else { + _logger.warning( + 'Chunk ${i + 1} is corrupted: expected $expectedSize bytes, ' + 'but got $actualSize bytes', + ); + existingChunks[i] = false; + await chunkFile.delete(); // Remove corrupted chunk + } + } + + return existingChunks; + } + + int _calculateDownloadedBytes( + List existingChunks, + int totalBytes, + int totalChunks, + ) { + int bytes = 0; + for (int i = 0; i < existingChunks.length; i++) { + if (existingChunks[i]) { + bytes += i == totalChunks - 1 + ? totalBytes - (i * downloadChunkSize) + : downloadChunkSize; + } + } + return bytes; + } + + Future _downloadChunk( + DownloadTask task, + String basePath, + int chunkIndex, + int totalChunks, + CancelToken cancelToken, + ) async { + final chunkPath = _getChunkPath(basePath, chunkIndex + 1); + final startByte = chunkIndex * downloadChunkSize; + final endByte = chunkIndex == totalChunks - 1 + ? task.totalBytes - 1 + : (startByte + downloadChunkSize) - 1; + + await _dio.download( + FileUrl.getUrl(task.id, FileUrlType.directDownload), + chunkPath, + options: Options( + headers: { + "X-Auth-Token": Configuration.instance.getToken(), + "Range": "bytes=$startByte-$endByte", + }, + ), + cancelToken: cancelToken, + onReceiveProgress: (received, total) async { + final updatedTask = task.copyWith( + bytesDownloaded: (chunkIndex) * downloadChunkSize + received, + ); + _notifyProgress(updatedTask); + }, + ); + // Update progress after chunk completion + final chunkFileSize = await File(chunkPath).length(); + task = task.copyWith( + bytesDownloaded: (chunkIndex) * downloadChunkSize + chunkFileSize, + ); + _updateTask(task); + } + + Future _combineChunks(String basePath, int totalChunks) async { + final finalFile = File(basePath); + final sink = finalFile.openWrite(); + try { + for (int i = 1; i <= totalChunks; i++) { + final chunkFile = File(_getChunkPath(basePath, i)); + final bytes = await chunkFile.readAsBytes(); + sink.add(bytes); + await chunkFile.delete(); + } + } finally { + await sink.close(); + } + return finalFile.path; + } + + Future _deleteFiles(DownloadTask task) async { + try { + final directory = Configuration.instance.getTempDirectory(); + final basePath = '$directory${task.id}.encrypted'; + final finalFile = File(basePath); + if (await finalFile.exists()) await finalFile.delete(); + + // Delete chunk files + final totalChunks = (task.totalBytes / downloadChunkSize).ceil(); + for (int i = 1; i <= totalChunks; i++) { + final chunkFile = File(_getChunkPath(basePath, i)); + if (await chunkFile.exists()) await chunkFile.delete(); + } + } catch (e) { + _logger.warning('Error deleting files: $e'); + } + } + + void _updateTask(DownloadTask task) { + _tasks[task.id] = task; + _notifyProgress(task); + } + + void _notifyProgress(DownloadTask task) { + final stream = _streams[task.id]; + if (stream != null && !stream.isClosed) { + stream.add(task); + } + } + + void _cleanup(int fileId) { + _completers.remove(fileId); + _cancelTokens.remove(fileId); + + final stream = _streams[fileId]; + if (stream != null && !stream.hasListener) { + stream.close(); + _streams.remove(fileId); + } + } + + Future dispose() async { + for (final completer in _completers.values) { + if (!completer.isCompleted) { + completer.completeError('Disposed'); + } + } + _completers.clear(); + + for (final token in _cancelTokens.values) { + token.cancel('Disposed'); + } + _cancelTokens.clear(); + + for (final stream in _streams.values) { + await stream.close(); + } + _streams.clear(); + + _tasks.clear(); + } +} diff --git a/mobile/apps/locker/lib/services/files/download/models/task.dart b/mobile/apps/locker/lib/services/files/download/models/task.dart new file mode 100644 index 0000000000..1bd96ca295 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/download/models/task.dart @@ -0,0 +1,80 @@ +enum DownloadStatus { + pending, + downloading, + paused, + completed, + error, + cancelled +} + +class DownloadTask { + final int id; + final String filename; + final int totalBytes; + int bytesDownloaded; + DownloadStatus status; + String? error; + String? filePath; + + DownloadTask({ + required this.id, + required this.filename, + required this.totalBytes, + this.bytesDownloaded = 0, + this.status = DownloadStatus.pending, + this.error, + this.filePath, + }); + + double get progress => totalBytes > 0 ? bytesDownloaded / totalBytes : 0.0; + bool get isCompleted => status == DownloadStatus.completed; + bool get isActive => status == DownloadStatus.downloading; + bool get isFinished => [ + DownloadStatus.completed, + DownloadStatus.error, + DownloadStatus.cancelled, + ].contains(status); + + Map toMap() => { + 'id': id, + 'filename': filename, + 'totalBytes': totalBytes, + 'bytesDownloaded': bytesDownloaded, + 'status': status.name, + 'error': error, + 'filePath': filePath, + }; + + static DownloadTask fromMap(Map map) => DownloadTask( + id: map['id'], + filename: map['filename'], + totalBytes: map['totalBytes'], + bytesDownloaded: map['bytesDownloaded'] ?? 0, + status: DownloadStatus.values.byName(map['status']), + error: map['error'], + filePath: map['filePath'], + ); + + DownloadTask copyWith({ + int? bytesDownloaded, + DownloadStatus? status, + String? error, + String? filePath, + }) => + DownloadTask( + id: id, + filename: filename, + totalBytes: totalBytes, + bytesDownloaded: bytesDownloaded ?? this.bytesDownloaded, + status: status ?? this.status, + error: error ?? this.error, + filePath: filePath ?? this.filePath, + ); +} + +class DownloadResult { + final DownloadTask task; + final bool success; + + DownloadResult(this.task, this.success); +} diff --git a/mobile/apps/locker/lib/services/files/download/service_locator.dart b/mobile/apps/locker/lib/services/files/download/service_locator.dart new file mode 100644 index 0000000000..5de5f31b4e --- /dev/null +++ b/mobile/apps/locker/lib/services/files/download/service_locator.dart @@ -0,0 +1,36 @@ +import 'package:dio/dio.dart'; +import 'package:locker/services/files/download/manager.dart'; +import 'package:package_info_plus/package_info_plus.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ServiceLocator { + late final SharedPreferences prefs; + late final Dio enteDio; + late final Dio nonEnteDio; + late final PackageInfo packageInfo; + + // instance + ServiceLocator._privateConstructor(); + + static final ServiceLocator instance = ServiceLocator._privateConstructor(); + + init( + SharedPreferences prefs, + Dio enteDio, + Dio nonEnteDio, + PackageInfo packageInfo, + ) { + this.prefs = prefs; + this.enteDio = enteDio; + this.nonEnteDio = nonEnteDio; + this.packageInfo = packageInfo; + } +} + +DownloadManager? _downloadManager; +DownloadManager get downloadManager { + _downloadManager ??= DownloadManager( + ServiceLocator.instance.nonEnteDio, + ); + return _downloadManager!; +} diff --git a/mobile/apps/locker/lib/services/files/links/links_client.dart b/mobile/apps/locker/lib/services/files/links/links_client.dart new file mode 100644 index 0000000000..ca7b6ec8c4 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/links/links_client.dart @@ -0,0 +1,39 @@ +import "package:ente_network/network.dart"; +import "package:locker/services/files/links/models/shareable_link.dart"; +import "package:logging/logging.dart"; + +class LinksClient { + LinksClient._(); + + static final LinksClient instance = LinksClient._(); + + final _logger = Logger("LinksClient"); + final _enteDio = Network.instance.enteDio; + + Future init() async {} + + Future getOrCreateLink(int fileID) async { + try { + final response = await _enteDio.post( + '/files/share-url', + data: { + 'fileID': fileID, + 'app': 'locker', + }, + ); + return ShareableLink.fromJson(response.data as Map); + } catch (e, s) { + _logger.severe('Failed to get or create link for file ID: $fileID', e, s); + rethrow; + } + } + + Future deleteLink(int linkID) async { + try { + await _enteDio.delete('/files/share-url/$linkID'); + } catch (e, s) { + _logger.severe('Failed to delete link with ID: $linkID', e, s); + rethrow; + } + } +} diff --git a/mobile/apps/locker/lib/services/files/links/links_service.dart b/mobile/apps/locker/lib/services/files/links/links_service.dart new file mode 100644 index 0000000000..9114e0c918 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/links/links_service.dart @@ -0,0 +1,29 @@ +import "package:fast_base58/fast_base58.dart"; +import "package:locker/services/collections/collections_service.dart"; +import "package:locker/services/files/links/links_client.dart"; +import "package:locker/services/files/links/models/shareable_link.dart"; +import "package:locker/services/files/sync/models/file.dart"; + +class LinksService { + LinksService._(); + + static final LinksService instance = LinksService._(); + + late final LinksClient _client; + + Future init() async { + _client = LinksClient.instance; + } + + Future getOrCreateLink(EnteFile file) async { + final link = await _client.getOrCreateLink(file.uploadedFileID!); + link.fullURL = link.url + + "#" + + Base58Encode(await CollectionService.instance.getFileKey(file)); + return link; + } + + Future deleteLink(int fileID) async { + await _client.deleteLink(fileID); + } +} diff --git a/mobile/apps/locker/lib/services/files/links/models/shareable_link.dart b/mobile/apps/locker/lib/services/files/links/models/shareable_link.dart new file mode 100644 index 0000000000..9e71fdd401 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/links/models/shareable_link.dart @@ -0,0 +1,148 @@ +class ShareableLink { + final String linkID; + final String url; + final int ownerID; + final int fileID; + final int? validTill; + final int? deviceLimit; + final bool passwordEnabled; + final String? nonce; + final int? memLimit; + final int? opsLimit; + final bool enableDownload; + final int createdAt; + String? fullURL; + + ShareableLink({ + required this.linkID, + required this.url, + required this.ownerID, + required this.fileID, + this.validTill, + this.deviceLimit, + required this.passwordEnabled, + this.nonce, + this.memLimit, + this.opsLimit, + required this.enableDownload, + required this.createdAt, + }); + + factory ShareableLink.fromJson(Map json) { + return ShareableLink( + linkID: json['linkID'] as String, + url: json['url'] as String, + ownerID: json['ownerID'] as int, + fileID: json['fileID'] as int, + validTill: json['validTill'] as int?, + deviceLimit: json['deviceLimit'] as int?, + passwordEnabled: json['passwordEnabled'] as bool, + nonce: json['nonce'] as String?, + memLimit: json['memLimit'] as int?, + opsLimit: json['opsLimit'] as int?, + enableDownload: json['enableDownload'] as bool, + createdAt: json['createdAt'] as int, + ); + } + + Map toJson() { + return { + 'linkID': linkID, + 'url': url, + 'ownerID': ownerID, + 'fileID': fileID, + if (validTill != null) 'validTill': validTill, + if (deviceLimit != null) 'deviceLimit': deviceLimit, + 'passwordEnabled': passwordEnabled, + if (nonce != null) 'nonce': nonce, + if (memLimit != null) 'memLimit': memLimit, + if (opsLimit != null) 'opsLimit': opsLimit, + 'enableDownload': enableDownload, + 'createdAt': createdAt, + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + return other is ShareableLink && + other.linkID == linkID && + other.url == url && + other.ownerID == ownerID && + other.fileID == fileID && + other.validTill == validTill && + other.deviceLimit == deviceLimit && + other.passwordEnabled == passwordEnabled && + other.nonce == nonce && + other.memLimit == memLimit && + other.opsLimit == opsLimit && + other.enableDownload == enableDownload && + other.createdAt == createdAt; + } + + @override + int get hashCode { + return Object.hash( + linkID, + url, + ownerID, + fileID, + validTill, + deviceLimit, + passwordEnabled, + nonce, + memLimit, + opsLimit, + enableDownload, + createdAt, + ); + } + + @override + String toString() { + return 'FileUrl(' + 'linkID: $linkID, ' + 'url: $url, ' + 'ownerID: $ownerID, ' + 'fileID: $fileID, ' + 'validTill: $validTill, ' + 'deviceLimit: $deviceLimit, ' + 'passwordEnabled: $passwordEnabled, ' + 'nonce: $nonce, ' + 'memLimit: $memLimit, ' + 'opsLimit: $opsLimit, ' + 'enableDownload: $enableDownload, ' + 'createdAt: $createdAt' + ')'; + } + + ShareableLink copyWith({ + String? linkID, + String? url, + int? ownerID, + int? fileID, + int? validTill, + int? deviceLimit, + bool? passwordEnabled, + String? nonce, + int? memLimit, + int? opsLimit, + bool? enableDownload, + int? createdAt, + }) { + return ShareableLink( + linkID: linkID ?? this.linkID, + url: url ?? this.url, + ownerID: ownerID ?? this.ownerID, + fileID: fileID ?? this.fileID, + validTill: validTill ?? this.validTill, + deviceLimit: deviceLimit ?? this.deviceLimit, + passwordEnabled: passwordEnabled ?? this.passwordEnabled, + nonce: nonce ?? this.nonce, + memLimit: memLimit ?? this.memLimit, + opsLimit: opsLimit ?? this.opsLimit, + enableDownload: enableDownload ?? this.enableDownload, + createdAt: createdAt ?? this.createdAt, + ); + } +} diff --git a/mobile/apps/locker/lib/services/files/sync/metadata_updater_service.dart b/mobile/apps/locker/lib/services/files/sync/metadata_updater_service.dart new file mode 100644 index 0000000000..c9bdc14fc5 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/sync/metadata_updater_service.dart @@ -0,0 +1,202 @@ +import 'dart:convert'; + +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_network/network.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/files/sync/models/file_magic.dart'; +import 'package:logging/logging.dart'; + +class MetadataUpdaterService { + MetadataUpdaterService._privateConstructor(); + + static final MetadataUpdaterService instance = + MetadataUpdaterService._privateConstructor(); + + Future init() async {} + + final _logger = Logger("MetadataUpdaterService"); + final _enteDio = Network.instance.enteDio; + + Future editFileCaption(EnteFile file, String caption) async { + try { + await _updatePublicMetadata([file], captionKey, caption); + await CollectionService.instance.sync(); + return true; + } catch (e) { + return false; + } + } + + Future editFileNameAndCaption( + EnteFile file, + String name, + String caption, + ) async { + try { + final Map updates = { + editNameKey: name, + captionKey: caption, + }; + await _updatePublicMetadataBulk([file], updates); + await CollectionService.instance.sync(); + return true; + } catch (e) { + return false; + } + } + + Future _updatePublicMetadata( + List files, + String key, + dynamic value, + ) async { + if (files.isEmpty) { + return; + } + try { + final Map update = {key: value}; + await _updatePublicMagicMetadata(files, update); + } catch (e, s) { + _logger.severe( + "Failed to update public metadata for files: $files", e, s,); + rethrow; + } + } + + Future _updatePublicMetadataBulk( + List files, + Map updates, + ) async { + if (files.isEmpty) { + return; + } + try { + await _updatePublicMagicMetadata(files, updates); + } catch (e, s) { + _logger.severe( + "Failed to update public metadata for files: $files", e, s,); + rethrow; + } + } + + Future _updatePublicMagicMetadata( + List files, + Map? newMetadataUpdate, { + Map>? metadataUpdateMap, + }) async { + final params = {}; + params['metadataList'] = []; + final int ownerID = Configuration.instance.getUserID()!; + try { + for (final file in files) { + if (file.uploadedFileID == null) { + throw AssertionError( + "operation is only supported on backed up files", + ); + } else if (file.ownerID != ownerID) { + throw AssertionError("cannot modify memories not owned by you"); + } + // read the existing magic metadata and apply new updates to existing data + // current update is simple replace. This will be enhanced in the future, + // as required. + final newUpdates = metadataUpdateMap != null + ? metadataUpdateMap[file.uploadedFileID] + : newMetadataUpdate; + assert( + newUpdates != null && newUpdates.isNotEmpty, + "can not apply empty updates", + ); + final Map jsonToUpdate = + jsonDecode(file.pubMmdEncodedJson ?? '{}'); + newUpdates!.forEach((key, value) { + jsonToUpdate[key] = value; + }); + + // update the local information so that it's reflected on UI + file.pubMmdEncodedJson = jsonEncode(jsonToUpdate); + file.pubMagicMetadata = PubMagicMetadata.fromJson(jsonToUpdate); + + final fileKey = await CollectionService.instance.getFileKey(file); + + final encryptedMMd = await CryptoUtil.encryptData( + utf8.encode(jsonEncode(jsonToUpdate)), + fileKey, + ); + params['metadataList'].add( + UpdateMagicMetadataRequest( + id: file.uploadedFileID!, + magicMetadata: MetadataRequest( + version: file.pubMmdVersion, + count: jsonToUpdate.length, + data: CryptoUtil.bin2base64(encryptedMMd.encryptedData!), + header: CryptoUtil.bin2base64(encryptedMMd.header!), + ), + ), + ); + file.pubMmdVersion = file.pubMmdVersion + 1; + } + + await _enteDio.put("/files/public-magic-metadata", data: params); + } catch (e, s) { + _logger.severe(e, s); + rethrow; + } + } +} + +class UpdateMagicMetadataRequest { + final int id; + final MetadataRequest? magicMetadata; + + UpdateMagicMetadataRequest({required this.id, required this.magicMetadata}); + + factory UpdateMagicMetadataRequest.fromJson(dynamic json) { + return UpdateMagicMetadataRequest( + id: json['id'], + magicMetadata: json['magicMetadata'] != null + ? MetadataRequest.fromJson(json['magicMetadata']) + : null, + ); + } + + Map toJson() { + final map = {}; + map['id'] = id; + if (magicMetadata != null) { + map['magicMetadata'] = magicMetadata!.toJson(); + } + return map; + } +} + +class MetadataRequest { + int? version; + int? count; + String? data; + String? header; + + MetadataRequest({ + required this.version, + required this.count, + required this.data, + required this.header, + }); + + MetadataRequest.fromJson(dynamic json) { + version = json['version']; + count = json['count']; + data = json['data']; + header = json['header']; + } + + Map toJson() { + final map = {}; + map['version'] = version; + map['count'] = count; + map['data'] = data; + map['header'] = header; + return map; + } +} diff --git a/mobile/apps/locker/lib/services/files/sync/models/common_keys.dart b/mobile/apps/locker/lib/services/files/sync/models/common_keys.dart new file mode 100644 index 0000000000..1156623e8a --- /dev/null +++ b/mobile/apps/locker/lib/services/files/sync/models/common_keys.dart @@ -0,0 +1,8 @@ +const magicKeyVisibility = 'visibility'; + +// Visibility Constants +const visibleVisibility = 0; +const archiveVisibility = 1; + +///Do not use [hiddenVisibility] for hidden files +const hiddenVisibility = 2; diff --git a/mobile/apps/locker/lib/services/files/sync/models/file.dart b/mobile/apps/locker/lib/services/files/sync/models/file.dart new file mode 100644 index 0000000000..50046746e3 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/sync/models/file.dart @@ -0,0 +1,123 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:locker/services/files/download/file_url.dart'; +import 'package:locker/services/files/sync/models/file_magic.dart'; +import 'package:logging/logging.dart'; + +class EnteFile { + String? localPath; + int? uploadedFileID; + int? ownerID; + int? collectionID; + String? title; + int? creationTime; + int? modificationTime; + int? updationTime; + int? addedTime; + String? hash; + int? metadataVersion; + String? encryptedKey; + String? keyDecryptionNonce; + String? fileDecryptionHeader; + String? thumbnailDecryptionHeader; + String? metadataDecryptionHeader; + int? fileSize; + + String? mMdEncodedJson; + int mMdVersion = 0; + MagicMetadata? _mmd; + + MagicMetadata get magicMetadata => + _mmd ?? MagicMetadata.fromEncodedJson(mMdEncodedJson ?? '{}'); + + set magicMetadata(val) => _mmd = val; + + String? pubMmdEncodedJson; + int pubMmdVersion = 1; + PubMagicMetadata? _pubMmd; + + PubMagicMetadata get pubMagicMetadata => + _pubMmd ?? PubMagicMetadata.fromEncodedJson(pubMmdEncodedJson ?? '{}'); + + set pubMagicMetadata(val) => _pubMmd = val; + + static const kCurrentMetadataVersion = 2; + + static final _logger = Logger('File'); + + EnteFile(); + + static EnteFile fromFile(File file) { + final enteFile = EnteFile(); + enteFile.localPath = file.path; + enteFile.title = file.path.split('/').last; + enteFile.creationTime = file.statSync().changed.millisecondsSinceEpoch; + enteFile.modificationTime = file.statSync().modified.millisecondsSinceEpoch; + return enteFile; + } + + Map get metadata { + final metadata = {}; + metadata["title"] = title; + metadata["localPath"] = localPath; + metadata["creationTime"] = creationTime; + metadata["modificationTime"] = modificationTime; + if (hash != null) { + metadata["hash"] = hash; + } + if (metadataVersion != null) { + metadata["version"] = metadataVersion; + } + return metadata; + } + + String get downloadUrl => + FileUrl.getUrl(uploadedFileID!, FileUrlType.download); + + String? get caption { + return pubMagicMetadata.caption; + } + + String? debugCaption; + + String get displayName { + if (pubMagicMetadata.editedName != null) { + return pubMagicMetadata.editedName!; + } + if (title == null && kDebugMode) _logger.severe('File title is null'); + return title ?? ''; + } + + bool get isUploaded { + return uploadedFileID != null; + } + + void applyMetadata(Map metadata) { + title = metadata["title"]; + localPath = metadata["localPath"]; + creationTime = metadata["creationTime"] ?? 0; + modificationTime = metadata["modificationTime"] ?? creationTime; + hash = metadata["hash"]; + metadataVersion = metadata["version"] ?? 0; + } + + @override + String toString() { + return '''File(title: $title, uploadedFileId: $uploadedFileID, + modificationTime: $modificationTime, ownerID: $ownerID, + collectionID: $collectionID, updationTime: $updationTime)'''; + } + + @override + bool operator ==(Object o) { + if (identical(this, o)) return true; + + return o is EnteFile && o.uploadedFileID == uploadedFileID; + } + + @override + int get hashCode { + return uploadedFileID.hashCode; + } +} diff --git a/mobile/apps/locker/lib/services/files/sync/models/file_magic.dart b/mobile/apps/locker/lib/services/files/sync/models/file_magic.dart new file mode 100644 index 0000000000..f99f1e8e4f --- /dev/null +++ b/mobile/apps/locker/lib/services/files/sync/models/file_magic.dart @@ -0,0 +1,127 @@ +import "dart:convert"; + +import "package:flutter/cupertino.dart"; +import "package:locker/services/files/sync/models/common_keys.dart"; + +const editTimeKey = 'editedTime'; +const editNameKey = 'editedName'; +const captionKey = "caption"; +const uploaderNameKey = "uploaderName"; +const widthKey = 'w'; +const heightKey = 'h'; +const streamVersionKey = 'sv'; +const mediaTypeKey = 'mediaType'; +const latKey = "lat"; +const longKey = "long"; +const motionVideoIndexKey = "mvi"; +const noThumbKey = "noThumb"; +const dateTimeKey = 'dateTime'; +const offsetTimeKey = 'offsetTime'; + +class MagicMetadata { + // 0 -> visible + // 1 -> archived + // 2 -> hidden etc? + int visibility; + + MagicMetadata({required this.visibility}); + + factory MagicMetadata.fromEncodedJson(String encodedJson) => + MagicMetadata.fromJson(jsonDecode(encodedJson)); + + factory MagicMetadata.fromJson(dynamic json) => MagicMetadata.fromMap(json); + + static fromMap(Map? map) { + if (map == null) return null; + return MagicMetadata( + visibility: map[magicKeyVisibility] ?? visibleVisibility, + ); + } +} + +class PubMagicMetadata { + int? editedTime; + String? editedName; + String? caption; + String? uploaderName; + int? w; + int? h; + double? lat; + double? long; + + // Indicates streaming version of the file. + // If this is set, then the file is a streaming version of the original file. + int? sv; + + // ISO 8601 datetime without timezone. This contains the date and time of the photo in the original tz + // where the photo was taken. + String? dateTime; + String? offsetTime; + + // Motion Video Index. Positive value (>0) indicates that the file is a motion + // photo + int? mvi; + + // if true, then the thumbnail is not available + // Note: desktop/web sets hasStaticThumbnail in the file metadata. + // As we don't want to support updating the og file metadata (yet), adding + // this new field to the pub metadata. For static thumbnail, all thumbnails + // should have exact same hash with should match the constant `blackThumbnailBase64` + bool? noThumb; + + // null -> not computed + // 0 -> normal + // 1 -> panorama + int? mediaType; + + PubMagicMetadata({ + this.editedTime, + this.editedName, + this.caption, + this.uploaderName, + this.w, + this.h, + this.lat, + this.long, + this.mvi, + this.noThumb, + this.mediaType, + this.dateTime, + this.offsetTime, + this.sv, + }); + + factory PubMagicMetadata.fromEncodedJson(String encodedJson) => + PubMagicMetadata.fromJson(jsonDecode(encodedJson)); + + factory PubMagicMetadata.fromJson(dynamic json) => + PubMagicMetadata.fromMap(json); + + static fromMap(Map? map) { + if (map == null) return null; + return PubMagicMetadata( + editedTime: map[editTimeKey], + editedName: map[editNameKey], + caption: map[captionKey], + uploaderName: map[uploaderNameKey], + w: safeParseInt(map[widthKey], widthKey), + h: safeParseInt(map[heightKey], heightKey), + lat: map[latKey], + long: map[longKey], + mvi: map[motionVideoIndexKey], + noThumb: map[noThumbKey], + mediaType: map[mediaTypeKey], + dateTime: map[dateTimeKey], + offsetTime: map[offsetTimeKey], + sv: safeParseInt(map[streamVersionKey], streamVersionKey), + ); + } + + static int? safeParseInt(dynamic value, String key) { + if (value == null) return null; + if (value is int) return value; + debugPrint("PubMagicMetadata key: $key Unexpected value: $value"); + if (value is String) return int.tryParse(value); + return null; + } +} diff --git a/mobile/apps/locker/lib/services/files/sync/models/metadata_request.dart b/mobile/apps/locker/lib/services/files/sync/models/metadata_request.dart new file mode 100644 index 0000000000..de0589e1ed --- /dev/null +++ b/mobile/apps/locker/lib/services/files/sync/models/metadata_request.dart @@ -0,0 +1,29 @@ +class MetadataRequest { + int? version; + int? count; + String? data; + String? header; + + MetadataRequest({ + required this.version, + required this.count, + required this.data, + required this.header, + }); + + MetadataRequest.fromJson(dynamic json) { + version = json['version']; + count = json['count']; + data = json['data']; + header = json['header']; + } + + Map toJson() { + final map = {}; + map['version'] = version; + map['count'] = count; + map['data'] = data; + map['header'] = header; + return map; + } +} diff --git a/mobile/apps/locker/lib/services/files/upload/file_upload_service.dart b/mobile/apps/locker/lib/services/files/upload/file_upload_service.dart new file mode 100644 index 0000000000..f56b4611cd --- /dev/null +++ b/mobile/apps/locker/lib/services/files/upload/file_upload_service.dart @@ -0,0 +1,684 @@ +import 'dart:async'; +import 'dart:collection'; +import 'dart:convert'; +import 'dart:io'; +import 'dart:math' as math; + +import 'package:collection/collection.dart'; +import 'package:dio/dio.dart'; +import 'package:ente_accounts/services/user_service.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import 'package:ente_events/event_bus.dart'; +import 'package:ente_network/network.dart'; +import 'package:flutter/foundation.dart'; +import 'package:locker/core/constants.dart'; +import 'package:locker/core/errors.dart'; +import 'package:locker/events/backup_updated_event.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/configuration.dart'; +import "package:locker/services/files/sync/metadata_updater_service.dart"; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/files/sync/models/file_magic.dart'; +import 'package:locker/services/files/upload/models/backup_item.dart'; +import 'package:locker/services/files/upload/models/backup_item_status.dart'; +import 'package:locker/services/files/upload/models/upload_url.dart'; +import "package:locker/utils/crypto_helper.dart"; +import 'package:locker/utils/data_util.dart'; +import 'package:logging/logging.dart'; +import "package:path/path.dart"; +import 'package:shared_preferences/shared_preferences.dart'; +import "package:uuid/uuid.dart"; + +class FileUploader { + static const kMaximumConcurrentUploads = 4; + static const kMaximumConcurrentVideoUploads = 2; + static const kMaximumThumbnailCompressionAttempts = 2; + static const kMaximumUploadAttempts = 4; + static const kMaxFileSize5Gib = 5368709120; + static const kBlockedUploadsPollFrequency = Duration(seconds: 2); + static const kFileUploadTimeout = Duration(minutes: 50); + static const k20MBStorageBuffer = 20 * 1024 * 1024; + static const _lastStaleFileCleanupTime = "lastStaleFileCleanupTime"; + + final _logger = Logger("FileUploader"); + final _dio = Network.instance.getDio(); + final _enteDio = Network.instance.enteDio; + final LinkedHashMap _queue = + LinkedHashMap(); + final LinkedHashMap _allBackups = + LinkedHashMap(); + final kSafeBufferForLockExpiry = const Duration(hours: 4).inMicroseconds; + final kBGTaskDeathTimeout = const Duration(seconds: 5).inMicroseconds; + final _uploadURLs = Queue(); + + LinkedHashMap get allBackups => _allBackups; + + // Maintains the count of files in the current upload session. + // Upload session is the period between the first entry into the _queue and last entry out of the _queue + int _totalCountInUploadSession = 0; + + // _uploadCounter indicates number of uploads which are currently in progress + int _uploadCounter = 0; + late SharedPreferences _prefs; + + // _hasInitiatedForceUpload is used to track if user attempted force upload + // where files are uploaded directly (without adding them to DB). In such + // cases, we don't want to clear the stale upload files. See #removeStaleFiles + // as it can result in clearing files which are still being force uploaded. + final bool _hasInitiatedForceUpload = false; + + FileUploader._privateConstructor(); + + static FileUploader instance = FileUploader._privateConstructor(); + + Future init(SharedPreferences preferences, bool isBackground) async { + _prefs = preferences; + final currentTime = DateTime.now().microsecondsSinceEpoch; + if (currentTime - (_prefs.getInt(_lastStaleFileCleanupTime) ?? 0) > + tempDirCleanUpInterval) { + await removeStaleFiles(); + await _prefs.setInt(_lastStaleFileCleanupTime, currentTime); + } + } + + Future upload(File file, Collection collection) { + _totalCountInUploadSession++; + final String path = file.path; + final completer = Completer(); + _queue[path] = FileUploadItem(file, collection, completer); + _allBackups[path] = BackupItem( + status: BackupItemStatus.inQueue, + file: file, + collection: collection, + completer: completer, + ); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + _pollQueue(); + return completer.future; + } + + int getCurrentSessionUploadCount() { + return _totalCountInUploadSession; + } + + void clearQueue(final Error reason) { + final List uploadsToBeRemoved = []; + _queue.entries + .where((entry) => entry.value.status == UploadStatus.notStarted) + .forEach((pendingUpload) { + uploadsToBeRemoved.add(pendingUpload.key); + }); + for (final id in uploadsToBeRemoved) { + _queue.remove(id)?.completer.completeError(reason); + _allBackups[id] = _allBackups[id]!.copyWith( + status: BackupItemStatus.retry, + error: reason, + ); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + } + _totalCountInUploadSession = 0; + } + + void clearCachedUploadURLs() { + _uploadURLs.clear(); + } + + void removeFromQueueWhere( + final bool Function(File) fn, + final Error reason, + ) { + final List uploadsToBeRemoved = []; + _queue.entries + .where((entry) => entry.value.status == UploadStatus.notStarted) + .forEach((pendingUpload) { + if (fn(pendingUpload.value.file)) { + uploadsToBeRemoved.add(pendingUpload.key); + } + }); + for (final id in uploadsToBeRemoved) { + _queue.remove(id)?.completer.completeError(reason); + _allBackups[id] = _allBackups[id]! + .copyWith(status: BackupItemStatus.retry, error: reason); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + } + _logger.info( + 'number of enteries removed from queue ${uploadsToBeRemoved.length}', + ); + _totalCountInUploadSession -= uploadsToBeRemoved.length; + } + + void _pollQueue() { + if (_queue.isEmpty) { + // Upload session completed + _totalCountInUploadSession = 0; + return; + } + if (_uploadCounter < kMaximumConcurrentUploads) { + final pendingEntry = _queue.entries + .firstWhereOrNull( + (entry) => entry.value.status == UploadStatus.notStarted, + ) + ?.value; + if (pendingEntry != null) { + pendingEntry.status = UploadStatus.inProgress; + _allBackups[pendingEntry.file.path] = + _allBackups[pendingEntry.file.path]! + .copyWith(status: BackupItemStatus.uploading); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + _encryptAndUploadFileToCollection( + pendingEntry.file, + pendingEntry.collection, + ); + } + } + } + + Future _encryptAndUploadFileToCollection( + File file, + Collection collection, { + bool forcedUpload = false, + }) async { + _uploadCounter++; + final path = file.path; + try { + final uploadedFile = + await _tryToUpload(file, collection, forcedUpload).timeout( + kFileUploadTimeout, + onTimeout: () { + final message = "Upload timed out for file $file"; + _logger.warning(message); + throw TimeoutException(message); + }, + ); + _queue.remove(path)!.completer.complete(uploadedFile); + _allBackups[path] = + _allBackups[path]!.copyWith(status: BackupItemStatus.uploaded); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + return uploadedFile; + } catch (e) { + _queue.remove(path)!.completer.completeError(e); + _allBackups[path] = + _allBackups[path]!.copyWith(status: BackupItemStatus.retry, error: e); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + return null; + } finally { + _uploadCounter--; + _pollQueue(); + } + } + + Future removeStaleFiles() async { + if (_hasInitiatedForceUpload) { + _logger.info( + "Force upload was initiated, skipping stale file cleanup", + ); + return; + } + try { + final String dir = Configuration.instance.getTempDirectory(); + // delete all files in the temp directory that start with upload_ and + // ends with .encrypted. Fetch files in async manner + final files = await Directory(dir).list().toList(); + final filesToDelete = files.where((file) { + return file.path.contains(uploadTempFilePrefix) && + file.path.contains(".encrypted"); + }); + if (filesToDelete.isNotEmpty) { + _logger.info('Deleting ${filesToDelete.length} stale upload files '); + for (final file in filesToDelete) { + await file.delete(); + } + } + } catch (e, s) { + _logger.severe("Failed to remove stale files", e, s); + } + } + + Future _tryToUpload( + File file, + Collection collection, + bool forcedUpload, + ) async { + if (_allBackups[file.path] != null && + _allBackups[file.path]!.status != BackupItemStatus.uploading) { + _allBackups[file.path] = _allBackups[file.path]!.copyWith( + status: BackupItemStatus.uploading, + ); + Bus.instance.fire(BackupUpdatedEvent(_allBackups)); + } + + final tempDirectory = Configuration.instance.getTempDirectory(); + final String uniqueID = + '${const Uuid().v4().toString()}_${file.path.split('/').last}'; + + final encryptedFilePath = + '$tempDirectory$uploadTempFilePrefix${uniqueID}_file.encrypted'; + final encryptedThumbnailPath = + '$tempDirectory$uploadTempFilePrefix${uniqueID}_thumb.encrypted'; + late final int encFileSize; + + var uploadCompleted = false; + // This flag is used to decide whether to clear the iOS origin file cache + // or not. + var uploadHardFailure = false; + try { + _logger.info( + 'starting ${forcedUpload ? 'forced' : ''} ' + 'upload of ${file.toString()}', + ); + + Uint8List? key; + final encryptedFileExists = File(encryptedFilePath).existsSync(); + + if (encryptedFileExists) { + // otherwise just delete the file for singlepart upload + await File(encryptedFilePath).delete(); + } + await _checkIfWithinStorageLimit(file); + final encryptedFile = File(encryptedFilePath); + + final fileAttributes = await CryptoUtil.encryptFile( + file.path, + encryptedFilePath, + key: key, + ); + encFileSize = await encryptedFile.length(); + + final thumbnailData = base64Decode(blackThumbnailBase64); + final encryptedThumbnailData = await CryptoUtil.encryptData( + thumbnailData, + fileAttributes.key!, + ); + if (File(encryptedThumbnailPath).existsSync()) { + await File(encryptedThumbnailPath).delete(); + } + final encryptedThumbnailFile = File(encryptedThumbnailPath); + await encryptedThumbnailFile + .writeAsBytes(encryptedThumbnailData.encryptedData!); + final encThumbSize = await encryptedThumbnailFile.length(); + + final thumbnailUploadURL = await _getUploadURL(); + final thumbnailObjectKey = await _putFile( + thumbnailUploadURL, + encryptedThumbnailFile, + encThumbSize, + ); + + final fileUploadURL = await _getUploadURL(); + final fileObjectKey = + await _putFile(fileUploadURL, encryptedFile, encFileSize); + + final enteFile = EnteFile.fromFile(file); + + final encryptedMetadataResult = await CryptoUtil.encryptData( + utf8.encode(jsonEncode(enteFile.metadata)), + fileAttributes.key!, + ); + final fileDecryptionHeader = + CryptoUtil.bin2base64(fileAttributes.header!); + final thumbnailDecryptionHeader = + CryptoUtil.bin2base64(encryptedThumbnailData.header!); + final encryptedMetadata = CryptoUtil.bin2base64( + encryptedMetadataResult.encryptedData!, + ); + final metadataDecryptionHeader = + CryptoUtil.bin2base64(encryptedMetadataResult.header!); + final encryptedFileKeyData = CryptoUtil.encryptSync( + fileAttributes.key!, + CryptoHelper.instance.getCollectionKey(collection), + ); + final encryptedKey = + CryptoUtil.bin2base64(encryptedFileKeyData.encryptedData!); + final keyDecryptionNonce = + CryptoUtil.bin2base64(encryptedFileKeyData.nonce!); + final Map pubMetadata = {}; + pubMetadata["noThumb"] = true; + MetadataRequest? pubMetadataRequest; + if (pubMetadata.isNotEmpty) { + pubMetadataRequest = await getPubMetadataRequest( + enteFile, + pubMetadata, + fileAttributes.key!, + ); + } + final remoteFile = await _uploadFile( + enteFile, + collection.id, + encryptedKey, + keyDecryptionNonce, + fileObjectKey, + fileDecryptionHeader, + encFileSize, + thumbnailObjectKey, + thumbnailDecryptionHeader, + encThumbSize, + encryptedMetadata, + metadataDecryptionHeader, + pubMetadata: pubMetadataRequest, + ); + _logger.info("File upload complete for $remoteFile"); + uploadCompleted = true; + return remoteFile; + } catch (e, s) { + if (!(e is NoActiveSubscriptionError || + e is StorageLimitExceededError || + e is WiFiUnavailableError || + e is SilentlyCancelUploadsError || + e is InvalidFileError || + e is FileTooLargeForPlanError)) { + _logger.severe("File upload failed for $file", e, s); + } + if (e is InvalidFileError) { + _logger.severe("File upload ignored for $file", e); + } + if ((e is StorageLimitExceededError || + e is FileTooLargeForPlanError || + e is NoActiveSubscriptionError)) { + // file upload can not be retried in such cases without user intervention + uploadHardFailure = true; + } + rethrow; + } finally { + await _onUploadDone( + file, + uploadCompleted, + uploadHardFailure, + encryptedFilePath, + encryptedThumbnailPath, + ); + } + } + + Future getPubMetadataRequest( + EnteFile file, + Map newData, + Uint8List fileKey, + ) async { + final Map jsonToUpdate = + jsonDecode(file.pubMmdEncodedJson ?? '{}'); + newData.forEach((key, value) { + jsonToUpdate[key] = value; + }); + + // update the local information so that it's reflected on UI + file.pubMmdEncodedJson = jsonEncode(jsonToUpdate); + file.pubMagicMetadata = PubMagicMetadata.fromJson(jsonToUpdate); + final encryptedMMd = await CryptoUtil.encryptData( + utf8.encode(jsonEncode(jsonToUpdate)), + fileKey, + ); + return MetadataRequest( + version: file.pubMmdVersion == 0 ? 1 : file.pubMmdVersion, + count: jsonToUpdate.length, + data: CryptoUtil.bin2base64(encryptedMMd.encryptedData!), + header: CryptoUtil.bin2base64(encryptedMMd.header!), + ); + } + + Future _onUploadDone( + File sourceFile, + bool uploadCompleted, + bool uploadHardFailure, + String encryptedFilePath, + String encryptedThumbnailPath, + ) async { + // Note: Consider removing source file if upload has completed / failed + if (File(encryptedFilePath).existsSync()) { + await File(encryptedFilePath).delete(); + } + if (File(encryptedThumbnailPath).existsSync()) { + await File(encryptedThumbnailPath).delete(); + } + } + + /* + _checkIfWithinStorageLimit verifies if the file size for encryption and upload + is within the storage limit. It throws StorageLimitExceededError if the limit + is exceeded. This check is best effort and may not be completely accurate + due to UserDetail cache. It prevents infinite loops when clients attempt to + upload files that exceed the server's storage limit + buffer. + Note: Local storageBuffer is 20MB, server storageBuffer is 50MB, and an + additional 30MB is reserved for thumbnails and encryption overhead. + */ + Future _checkIfWithinStorageLimit(File fileToBeUploaded) async { + try { + final userDetails = UserService.instance.getCachedUserDetails(); + if (userDetails == null) { + return; + } + // add k20MBStorageBuffer to the free storage + final num freeStorage = userDetails.getFreeStorage() + k20MBStorageBuffer; + final num fileSize = await fileToBeUploaded.length(); + if (fileSize > freeStorage) { + _logger.warning('Storage limit exceeded fileSize $fileSize and ' + 'freeStorage $freeStorage'); + throw StorageLimitExceededError(); + } + if (fileSize > kMaxFileSize5Gib) { + _logger.warning('File size exceeds 5GiB fileSize $fileSize'); + throw InvalidFileError( + 'file size above 5GiB', + InvalidReason.tooLargeFile, + ); + } + } catch (e) { + if (e is StorageLimitExceededError || e is InvalidFileError) { + rethrow; + } else { + _logger.severe('Error checking storage limit', e); + } + } + } + + Future _uploadFile( + EnteFile file, + int collectionID, + String encryptedKey, + String keyDecryptionNonce, + String fileObjectKey, + String fileDecryptionHeader, + int fileSize, + String thumbnailObjectKey, + String thumbnailDecryptionHeader, + int thumbnailSize, + String encryptedMetadata, + String metadataDecryptionHeader, { + MetadataRequest? pubMetadata, + int attempt = 1, + }) async { + final request = { + "collectionID": collectionID, + "encryptedKey": encryptedKey, + "keyDecryptionNonce": keyDecryptionNonce, + "file": { + "objectKey": fileObjectKey, + "decryptionHeader": fileDecryptionHeader, + "size": fileSize, + }, + "thumbnail": { + "objectKey": thumbnailObjectKey, + "decryptionHeader": thumbnailDecryptionHeader, + "size": thumbnailSize, + }, + "metadata": { + "encryptedData": encryptedMetadata, + "decryptionHeader": metadataDecryptionHeader, + }, + }; + if (pubMetadata != null) { + request["pubMagicMetadata"] = pubMetadata; + } + try { + final response = await _enteDio.post("/files", data: request); + final data = response.data; + file.uploadedFileID = data["id"]; + file.collectionID = collectionID; + file.updationTime = data["updationTime"]; + file.ownerID = data["ownerID"]; + file.encryptedKey = encryptedKey; + file.keyDecryptionNonce = keyDecryptionNonce; + file.thumbnailDecryptionHeader = thumbnailDecryptionHeader; + file.fileDecryptionHeader = fileDecryptionHeader; + file.metadataDecryptionHeader = metadataDecryptionHeader; + return file; + } on DioException catch (e) { + final int statusCode = e.response?.statusCode ?? -1; + if (statusCode == 413) { + throw FileTooLargeForPlanError(); + } else if (statusCode == 426) { + _onStorageLimitExceeded(); + } else if (attempt < kMaximumUploadAttempts && statusCode == -1) { + // retry when DioException contains no response/status code + _logger.info( + "Upload file (${file.displayName}) failed, will retry in 3 seconds", + ); + await Future.delayed(const Duration(seconds: 3)); + return _uploadFile( + file, + collectionID, + encryptedKey, + keyDecryptionNonce, + fileObjectKey, + fileDecryptionHeader, + fileSize, + thumbnailObjectKey, + thumbnailDecryptionHeader, + thumbnailSize, + encryptedMetadata, + metadataDecryptionHeader, + attempt: attempt + 1, + pubMetadata: pubMetadata, + ); + } else { + _logger.severe("Failed to upload file ${file.displayName}", e); + } + rethrow; + } + } + + Future _getUploadURL() async { + if (_uploadURLs.isEmpty) { + // the queue is empty, fetch at least for one file to handle force uploads + // that are not in the queue. This is to also avoid + await fetchUploadURLs(math.max(_queue.length, 1)); + } + try { + return _uploadURLs.removeFirst(); + } catch (e) { + if (e is StateError && e.message == 'No element' && _queue.isEmpty) { + _logger.warning("Oops, uploadUrls has no element now, fetching again"); + return _getUploadURL(); + } else { + rethrow; + } + } + } + + Future? _uploadURLFetchInProgress; + + Future fetchUploadURLs(int fileCount) async { + _uploadURLFetchInProgress ??= Future(() async { + try { + final response = await _enteDio.get( + "/files/upload-urls", + queryParameters: { + "count": math.min(42, fileCount * 2), // m4gic number + }, + ); + final urls = (response.data["urls"] as List) + .map((e) => UploadURL.fromMap(e)) + .toList(); + _uploadURLs.addAll(urls); + } on DioException catch (e, s) { + if (e.response != null) { + if (e.response!.statusCode == 402) { + final error = NoActiveSubscriptionError(); + clearQueue(error); + throw error; + } else if (e.response!.statusCode == 426) { + final error = StorageLimitExceededError(); + clearQueue(error); + throw error; + } else { + _logger.warning("Could not fetch upload URLs", e, s); + } + } + rethrow; + } finally { + _uploadURLFetchInProgress = null; + } + }); + return _uploadURLFetchInProgress; + } + + void _onStorageLimitExceeded() { + clearQueue(StorageLimitExceededError()); + throw StorageLimitExceededError(); + } + + Future _putFile( + UploadURL uploadURL, + File file, + int fileSize, { + int attempt = 1, + }) async { + final startTime = DateTime.now().millisecondsSinceEpoch; + final fileName = basename(file.path); + try { + await _dio.put( + uploadURL.url, + data: file.openRead(), + options: Options( + headers: { + Headers.contentLengthHeader: fileSize, + }, + ), + ); + _logger.info( + "Uploaded object $fileName of size: ${formatBytes(fileSize)} at speed: ${(fileSize / (DateTime.now().millisecondsSinceEpoch - startTime)).toStringAsFixed(2)} KB/s", + ); + + return uploadURL.objectKey; + } on DioException catch (e) { + if (e.message?.startsWith("HttpException: Content size") ?? false) { + rethrow; + } else if (attempt < kMaximumUploadAttempts) { + _logger.info("Upload failed for $fileName, retrying"); + final newUploadURL = await _getUploadURL(); + return _putFile( + newUploadURL, + file, + fileSize, + attempt: attempt + 1, + ); + } else { + _logger.info( + "Failed to upload file ${basename(file.path)} after $attempt attempts", + e, + ); + rethrow; + } + } + } +} + +class FileUploadItem { + final File file; + final Collection collection; + final Completer completer; + UploadStatus status; + + FileUploadItem( + this.file, + this.collection, + this.completer, { + this.status = UploadStatus.notStarted, + }); +} + +enum UploadStatus { notStarted, inProgress, inBackground, completed } + +enum ProcessType { + background, + foreground, +} diff --git a/mobile/apps/locker/lib/services/files/upload/models/backup_item.dart b/mobile/apps/locker/lib/services/files/upload/models/backup_item.dart new file mode 100644 index 0000000000..10cc725667 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/upload/models/backup_item.dart @@ -0,0 +1,62 @@ +import "dart:async"; +import "dart:io"; + +import "package:locker/services/collections/models/collection.dart"; +import "package:locker/services/files/sync/models/file.dart"; +import "package:locker/services/files/upload/models/backup_item_status.dart"; + +class BackupItem { + final BackupItemStatus status; + final File file; + final Collection collection; + final Completer? completer; + final Object? error; + + BackupItem({ + required this.status, + required this.file, + required this.collection, + required this.completer, + this.error, + }); + + BackupItem copyWith({ + BackupItemStatus? status, + File? file, + Collection? collection, + Completer? completer, + Object? error, + }) { + return BackupItem( + status: status ?? this.status, + file: file ?? this.file, + collection: collection ?? this.collection, + completer: completer ?? this.completer, + error: error ?? this.error, + ); + } + + @override + String toString() { + return 'BackupItem(status: $status, file: $file, collection: $collection, error: $error)'; + } + + @override + bool operator ==(covariant BackupItem other) { + if (identical(this, other)) return true; + + return other.status == status && + other.file == file && + other.collection == collection && + other.completer == completer && + other.error == error; + } + + @override + int get hashCode { + return status.hashCode ^ + file.hashCode ^ + collection.hashCode ^ + completer.hashCode; + } +} diff --git a/mobile/apps/locker/lib/services/files/upload/models/backup_item_status.dart b/mobile/apps/locker/lib/services/files/upload/models/backup_item_status.dart new file mode 100644 index 0000000000..f9de2673ce --- /dev/null +++ b/mobile/apps/locker/lib/services/files/upload/models/backup_item_status.dart @@ -0,0 +1,7 @@ +enum BackupItemStatus { + uploading, + inQueue, + retry, + inBackground, + uploaded, +} diff --git a/mobile/apps/locker/lib/services/files/upload/models/upload_url.dart b/mobile/apps/locker/lib/services/files/upload/models/upload_url.dart new file mode 100644 index 0000000000..49aa3b6ad7 --- /dev/null +++ b/mobile/apps/locker/lib/services/files/upload/models/upload_url.dart @@ -0,0 +1,26 @@ +import 'dart:convert'; + +class UploadURL { + final String url; + final String objectKey; + + UploadURL(this.url, this.objectKey); + Map toMap() { + return { + 'url': url, + 'objectKey': objectKey, + }; + } + + factory UploadURL.fromMap(Map map) { + return UploadURL( + map['url'], + map['objectKey'], + ); + } + + String toJson() => json.encode(toMap()); + + factory UploadURL.fromJson(String source) => + UploadURL.fromMap(json.decode(source)); +} diff --git a/mobile/apps/locker/lib/services/trash/models/trash_file.dart b/mobile/apps/locker/lib/services/trash/models/trash_file.dart new file mode 100644 index 0000000000..50537e7ac7 --- /dev/null +++ b/mobile/apps/locker/lib/services/trash/models/trash_file.dart @@ -0,0 +1,14 @@ +import 'package:locker/services/files/sync/models/file.dart'; + +class TrashFile extends EnteFile { + // time when file was put in the trash for first time + late int createdAt; + + // for non-deleted trash items, updateAt is usually equal to the latest time + // when the file was moved to trash + late int updateAt; + + // time after which will will be deleted from trash & user's storage usage + // will go down + late int deleteBy; +} diff --git a/mobile/apps/locker/lib/services/trash/models/trash_item_request.dart b/mobile/apps/locker/lib/services/trash/models/trash_item_request.dart new file mode 100644 index 0000000000..b4169f738d --- /dev/null +++ b/mobile/apps/locker/lib/services/trash/models/trash_item_request.dart @@ -0,0 +1,22 @@ +class TrashRequest { + final int fileID; + final int collectionID; + + TrashRequest(this.fileID, this.collectionID); + + factory TrashRequest.fromJson(Map json) { + return TrashRequest(json['fileID'], json['collectionID']); + } + + Map toJson() { + final Map data = {}; + data['fileID'] = fileID; + data['collectionID'] = collectionID; + return data; + } + + @override + String toString() { + return 'TrashItemRequest{fileID: $fileID, collectionID: $collectionID}'; + } +} diff --git a/mobile/apps/locker/lib/services/trash/trash_db.dart b/mobile/apps/locker/lib/services/trash/trash_db.dart new file mode 100644 index 0000000000..7557e3426a --- /dev/null +++ b/mobile/apps/locker/lib/services/trash/trash_db.dart @@ -0,0 +1,173 @@ +import "package:ente_base/models/database.dart"; +import 'package:locker/services/trash/models/trash_file.dart'; +import 'package:path/path.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:sqflite/sqflite.dart'; + +class TrashDB extends EnteBaseDatabase { + TrashDB._privateConstructor(); + + static final TrashDB instance = TrashDB._privateConstructor(); + + Database? _database; + + static const String _trashTable = 'trash_files'; + + Future init() async { + _database = await _initDatabase(); + } + + Future _initDatabase() async { + final documentsDirectory = await getApplicationDocumentsDirectory(); + final path = join(documentsDirectory.path, 'trash.db'); + + return await openDatabase( + path, + version: 1, + onCreate: _createTables, + ); + } + + Future _createTables(Database db, int version) async { + await db.execute(''' + CREATE TABLE $_trashTable ( + uploaded_file_id INTEGER PRIMARY KEY, + local_path TEXT, + owner_id INTEGER, + collection_id INTEGER, + title TEXT, + creation_time INTEGER, + modification_time INTEGER, + updation_time INTEGER, + added_time INTEGER, + hash TEXT, + metadata_version INTEGER, + encrypted_key TEXT, + key_decryption_nonce TEXT, + file_decryption_header TEXT, + thumbnail_decryption_header TEXT, + metadata_decryption_header TEXT, + file_size INTEGER, + m_md_encoded_json TEXT, + m_md_version INTEGER, + pub_mmd_encoded_json TEXT, + pub_mmd_version INTEGER, + created_at INTEGER NOT NULL, + update_at INTEGER NOT NULL, + delete_by INTEGER NOT NULL + ) + '''); + } + + Database get _db { + if (_database == null) { + throw Exception('TrashDB not initialized. Call init() first.'); + } + return _database!; + } + + Future insertMultiple(List trashFiles) async { + final batch = _db.batch(); + + for (final trashFile in trashFiles) { + batch.insert( + _trashTable, + _trashFileToMap(trashFile), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + await batch.commit(); + } + + Future delete(List uploadedFileIDs) async { + final batch = _db.batch(); + + for (final uploadedFileID in uploadedFileIDs) { + batch.delete( + _trashTable, + where: 'uploaded_file_id = ?', + whereArgs: [uploadedFileID], + ); + } + + await batch.commit(); + } + + Future> getAllTrashFiles() async { + final result = await _db.query(_trashTable); + return result.map((row) => _mapToTrashFile(row)).toList(); + } + + @override + Future clearTable() async { + await _db.delete(_trashTable); + } + + Map _trashFileToMap(TrashFile trashFile) { + return { + 'uploaded_file_id': trashFile.uploadedFileID!, + 'local_path': trashFile.localPath, + 'owner_id': trashFile.ownerID, + 'collection_id': trashFile.collectionID, + 'title': trashFile.title, + 'creation_time': trashFile.creationTime, + 'modification_time': trashFile.modificationTime, + 'updation_time': trashFile.updationTime, + 'added_time': trashFile.addedTime, + 'hash': trashFile.hash, + 'metadata_version': trashFile.metadataVersion, + 'encrypted_key': trashFile.encryptedKey, + 'key_decryption_nonce': trashFile.keyDecryptionNonce, + 'file_decryption_header': trashFile.fileDecryptionHeader, + 'thumbnail_decryption_header': trashFile.thumbnailDecryptionHeader, + 'metadata_decryption_header': trashFile.metadataDecryptionHeader, + 'file_size': trashFile.fileSize, + 'm_md_encoded_json': trashFile.mMdEncodedJson, + 'm_md_version': trashFile.mMdVersion, + 'pub_mmd_encoded_json': trashFile.pubMmdEncodedJson, + 'pub_mmd_version': trashFile.pubMmdVersion, + 'created_at': trashFile.createdAt, + 'update_at': trashFile.updateAt, + 'delete_by': trashFile.deleteBy, + }; + } + + TrashFile _mapToTrashFile(Map map) { + final trashFile = TrashFile(); + + trashFile.localPath = map['local_path']; + trashFile.uploadedFileID = map['uploaded_file_id']; + trashFile.ownerID = map['owner_id']; + trashFile.collectionID = map['collection_id']; + trashFile.title = map['title']; + trashFile.creationTime = map['creation_time']; + trashFile.modificationTime = map['modification_time']; + trashFile.updationTime = map['updation_time']; + trashFile.addedTime = map['added_time']; + trashFile.hash = map['hash']; + trashFile.metadataVersion = map['metadata_version']; + trashFile.encryptedKey = map['encrypted_key']; + trashFile.keyDecryptionNonce = map['key_decryption_nonce']; + trashFile.fileDecryptionHeader = map['file_decryption_header']; + trashFile.thumbnailDecryptionHeader = map['thumbnail_decryption_header']; + trashFile.metadataDecryptionHeader = map['metadata_decryption_header']; + trashFile.fileSize = map['file_size']; + trashFile.mMdEncodedJson = map['m_md_encoded_json']; + trashFile.mMdVersion = map['m_md_version'] ?? 0; + trashFile.pubMmdEncodedJson = map['pub_mmd_encoded_json']; + trashFile.pubMmdVersion = map['pub_mmd_version'] ?? 1; + + // TrashFile specific fields + trashFile.createdAt = map['created_at']; + trashFile.updateAt = map['update_at']; + trashFile.deleteBy = map['delete_by']; + + return trashFile; + } + + Future close() async { + await _database?.close(); + _database = null; + } +} diff --git a/mobile/apps/locker/lib/services/trash/trash_service.dart b/mobile/apps/locker/lib/services/trash/trash_service.dart new file mode 100644 index 0000000000..a6ef50f6b9 --- /dev/null +++ b/mobile/apps/locker/lib/services/trash/trash_service.dart @@ -0,0 +1,279 @@ +import 'dart:async'; +import 'dart:convert'; +import 'dart:math'; + +import 'package:dio/dio.dart'; +import 'package:ente_crypto_dart/ente_crypto_dart.dart'; +import "package:ente_events/event_bus.dart"; +import "package:ente_events/models/signed_in_event.dart"; +import 'package:ente_network/network.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/collections/models/collection_file_item.dart'; +import "package:locker/services/configuration.dart"; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/files/sync/models/file_magic.dart'; +import 'package:locker/services/trash/models/trash_file.dart'; +import 'package:locker/services/trash/trash_db.dart'; +import "package:locker/utils/crypto_helper.dart"; +import 'package:logging/logging.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class TrashService { + TrashService._privateConstructor(); + static final TrashService instance = TrashService._privateConstructor(); + + static const kLastTrashSyncTime = "last_trash_sync_time"; + final _logger = Logger("TrashService"); + late SharedPreferences _prefs; + late Dio _enteDio; + late TrashDB _trashDB; + + Future init(SharedPreferences preferences) async { + _prefs = preferences; + _enteDio = Network.instance.enteDio; + _trashDB = TrashDB.instance; + + if (Configuration.instance.hasConfiguredAccount()) { + unawaited(syncTrash()); + } else { + Bus.instance.on().listen((event) { + _logger.info("User signed in, starting initial trash sync."); + unawaited(syncTrash()); + }); + } + } + + Future syncTrash() async { + final lastSyncTime = _getSyncTime(); + _logger.fine('sync trash sinceTime : $lastSyncTime'); + final diff = await getTrashFilesDiff(lastSyncTime); + if (diff.trashedFiles.isNotEmpty) { + _logger.fine("inserting ${diff.trashedFiles.length} items in trash"); + await _trashDB.insertMultiple(diff.trashedFiles); + } + if (diff.deletedUploadIDs.isNotEmpty) { + _logger.fine("discard ${diff.deletedUploadIDs.length} deleted items"); + await _trashDB.delete(diff.deletedUploadIDs); + } + if (diff.restoredFiles.isNotEmpty) { + _logger.fine("discard ${diff.restoredFiles.length} restored items"); + await _trashDB + .delete(diff.restoredFiles.map((e) => e.uploadedFileID!).toList()); + } + + if (diff.lastSyncedTimeStamp != 0) { + await _setSyncTime(diff.lastSyncedTimeStamp); + } + if (diff.hasMore) { + return syncTrash(); + } + } + + Future> getTrashFiles() async { + return await _trashDB.getAllTrashFiles(); + } + + Future _setSyncTime(int time) async { + return _prefs.setInt(kLastTrashSyncTime, time); + } + + int _getSyncTime() { + return _prefs.getInt(kLastTrashSyncTime) ?? 0; + } + + Future getTrashFilesDiff(int sinceTime) async { + try { + final response = await _enteDio.get( + "/trash/v2/diff", + queryParameters: { + "sinceTime": sinceTime, + }, + ); + int latestUpdatedAtTime = 0; + final trashedFiles = []; + final deletedUploadIDs = []; + final restoredFiles = []; + + final diff = response.data["diff"] as List; + final bool hasMore = response.data["hasMore"] as bool; + final startTime = DateTime.now(); + for (final item in diff) { + final trash = TrashFile(); + trash.createdAt = item['createdAt']; + trash.updateAt = item['updatedAt']; + latestUpdatedAtTime = max(latestUpdatedAtTime, trash.updateAt); + if (item["isDeleted"]) { + deletedUploadIDs.add(item["file"]["id"]); + continue; + } + + trash.deleteBy = item['deleteBy']; + trash.uploadedFileID = item["file"]["id"]; + trash.collectionID = item["file"]["collectionID"]; + trash.updationTime = item["file"]["updationTime"]; + trash.ownerID = item["file"]["ownerID"]; + trash.encryptedKey = item["file"]["encryptedKey"]; + trash.keyDecryptionNonce = item["file"]["keyDecryptionNonce"]; + trash.fileDecryptionHeader = item["file"]["file"]["decryptionHeader"]; + trash.thumbnailDecryptionHeader = + item["file"]["thumbnail"]["decryptionHeader"]; + trash.metadataDecryptionHeader = + item["file"]["metadata"]["decryptionHeader"]; + // TODO: Refactor + final collections = await CollectionService.instance.getCollections(); + final Collection? collection = + collections.where((c) => c.id == trash.collectionID).isNotEmpty + ? collections.firstWhere((c) => c.id == trash.collectionID) + : null; + if (collection == null) { + continue; + } + final collectionKey = + CryptoHelper.instance.getCollectionKey(collection); + final key = CryptoHelper.instance.getFileKey( + trash.encryptedKey!, + trash.keyDecryptionNonce!, + collectionKey, + ); + final encodedMetadata = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item["file"]["metadata"]["encryptedData"]), + key, + CryptoUtil.base642bin(trash.metadataDecryptionHeader!), + ); + final Map metadata = + jsonDecode(utf8.decode(encodedMetadata)); + trash.applyMetadata(metadata); + if (item["file"]['magicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item["file"]['magicMetadata']['data']), + key, + CryptoUtil.base642bin(item["file"]['magicMetadata']['header']), + ); + trash.mMdEncodedJson = utf8.decode(utfEncodedMmd); + trash.mMdVersion = item["file"]['magicMetadata']['version']; + } + if (item["file"]['pubMagicMetadata'] != null) { + final utfEncodedMmd = await CryptoUtil.decryptData( + CryptoUtil.base642bin(item["file"]['pubMagicMetadata']['data']), + key, + CryptoUtil.base642bin(item["file"]['pubMagicMetadata']['header']), + ); + trash.pubMmdEncodedJson = utf8.decode(utfEncodedMmd); + trash.pubMmdVersion = item["file"]['pubMagicMetadata']['version']; + trash.pubMagicMetadata = + PubMagicMetadata.fromEncodedJson(trash.pubMmdEncodedJson!); + } + if (item['isRestored']) { + restoredFiles.add(trash); + continue; + } + trashedFiles.add(trash); + } + + final endTime = DateTime.now(); + _logger.info( + "time for parsing ${diff.length}: ${Duration( + microseconds: (endTime.microsecondsSinceEpoch - + startTime.microsecondsSinceEpoch), + ).inMilliseconds}", + ); + return TrashDiff( + trashedFiles, + restoredFiles, + deletedUploadIDs, + hasMore, + latestUpdatedAtTime, + ); + } catch (e, s) { + _logger.severe(e, s); + rethrow; + } + } + + Future deleteFromTrash(List files) async { + final params = {}; + final uniqueFileIds = files.map((e) => e.uploadedFileID!).toSet().toList(); + params["fileIDs"] = []; + for (final fileID in uniqueFileIds) { + params["fileIDs"].add(fileID); + } + try { + await _enteDio.post( + "/trash/delete", + data: params, + ); + await _trashDB.delete(uniqueFileIds); + } catch (e, s) { + _logger.severe("failed to delete from trash", e, s); + rethrow; + } + // no need to await on syncing trash from remote + unawaited(syncTrash()); + } + + Future emptyTrash() async { + final params = {}; + params["lastUpdatedAt"] = _getSyncTime(); + try { + await _enteDio.post( + "/trash/empty", + data: params, + ); + await _trashDB.clearTable(); + unawaited(syncTrash()); + } catch (e, s) { + _logger.severe("failed to empty trash", e, s); + rethrow; + } + } + + Future restore(List files, Collection toCollection) async { + final params = {}; + params["collectionID"] = toCollection.id; + final toCollectionKey = + CryptoHelper.instance.getCollectionKey(toCollection); + params["files"] = []; + for (final file in files) { + final fileKey = await CollectionService.instance.getFileKey(file); + file.collectionID = toCollection.id; + final encryptedKeyData = CryptoUtil.encryptSync(fileKey, toCollectionKey); + final encryptedKey = + CryptoUtil.bin2base64(encryptedKeyData.encryptedData!); + final keyDecryptionNonce = CryptoUtil.bin2base64(encryptedKeyData.nonce!); + params["files"].add( + CollectionFileItem( + file.uploadedFileID!, + encryptedKey, + keyDecryptionNonce, + ).toMap(), + ); + } + try { + await _enteDio.post( + "/collections/restore-files", + data: params, + ); + await _trashDB.delete(files.map((e) => e.uploadedFileID!).toList()); + // Force reload home gallery to pull in the restored files + } catch (e, s) { + _logger.severe("failed to restore files", e, s); + rethrow; + } + } +} + +class TrashDiff { + final List trashedFiles; + final List restoredFiles; + final List deletedUploadIDs; + final bool hasMore; + final int lastSyncedTimeStamp; + TrashDiff( + this.trashedFiles, + this.restoredFiles, + this.deletedUploadIDs, + this.hasMore, + this.lastSyncedTimeStamp, + ); +} diff --git a/mobile/apps/locker/lib/ui/components/collection_selection_widget.dart b/mobile/apps/locker/lib/ui/components/collection_selection_widget.dart new file mode 100644 index 0000000000..b263ddc960 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/collection_selection_widget.dart @@ -0,0 +1,250 @@ +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/utils/collection_actions.dart'; + +class CollectionSelectionWidget extends StatefulWidget { + final List collections; + final Set selectedCollectionIds; + final Function(int) onToggleCollection; + final Function(List)? onCollectionsUpdated; + + const CollectionSelectionWidget({ + super.key, + required this.collections, + required this.selectedCollectionIds, + required this.onToggleCollection, + this.onCollectionsUpdated, + }); + + @override + State createState() => + _CollectionSelectionWidgetState(); +} + +class _CollectionSelectionWidgetState extends State { + List _availableCollections = []; + + @override + void initState() { + super.initState(); + _availableCollections = List.from(widget.collections); + } + + @override + void didUpdateWidget(CollectionSelectionWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.collections != widget.collections) { + _availableCollections = List.from(widget.collections); + } + } + + Future _createNewCollection() async { + final newCollection = await CollectionActions.createCollection(context); + + if (newCollection != null) { + setState(() { + _availableCollections.add(newCollection); + }); + + widget.onToggleCollection(newCollection.id); + + widget.onCollectionsUpdated?.call(_availableCollections); + } + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Collections', + style: textTheme.small.copyWith( + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 8), + Container( + height: 150, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.strokeFaint), + ), + child: _availableCollections.isEmpty + ? Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + ), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + 'No collections available', + style: textTheme.body.copyWith( + color: colorScheme.textMuted, + ), + ), + const SizedBox(height: 8), + InkWell( + onTap: _createNewCollection, + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: colorScheme.primary300.withOpacity(0.1), + borderRadius: BorderRadius.circular(6), + border: Border.all( + color: colorScheme.primary500, + width: 1, + ), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.add, + size: 16, + color: colorScheme.primary700, + ), + const SizedBox(width: 4), + Text( + 'Create collection', + style: textTheme.small.copyWith( + color: colorScheme.primary700, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ), + ), + ], + ), + ), + ) + : Scrollbar( + thumbVisibility: true, + thickness: 6, + radius: const Radius.circular(3), + child: GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 3.5, + crossAxisSpacing: 6, + mainAxisSpacing: 6, + ), + padding: const EdgeInsets.all(6), + itemCount: _availableCollections.length + + 1, // +1 for "Create New" option + itemBuilder: (context, index) { + if (index < _availableCollections.length) { + final collection = _availableCollections[index]; + final isSelected = widget.selectedCollectionIds + .contains(collection.id); + final collectionName = + collection.name ?? 'Unnamed Collection'; + + return InkWell( + onTap: () => widget.onToggleCollection(collection.id), + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 4, + ), + decoration: BoxDecoration( + color: isSelected + ? colorScheme.primary300.withOpacity(0.3) + : colorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: isSelected + ? colorScheme.primary500 + : colorScheme.strokeFaint, + width: isSelected ? 2 : 1, + ), + ), + child: Center( + child: Text( + collectionName, + style: textTheme.small.copyWith( + color: isSelected + ? colorScheme.primary500 + : colorScheme.textBase, + fontWeight: isSelected + ? FontWeight.w600 + : FontWeight.normal, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ), + ), + ); + } + + return InkWell( + onTap: _createNewCollection, + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 4, + ), + decoration: BoxDecoration( + color: colorScheme.backgroundElevated, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: colorScheme.strokeFaint.withOpacity(0.5), + width: 1, + style: BorderStyle.solid, + ), + ), + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + Icons.add_outlined, + size: 14, + color: colorScheme.textBase, + ), + const SizedBox(width: 4), + Flexible( + child: Text( + 'Collection', + style: textTheme.small.copyWith( + color: colorScheme.textBase, + fontWeight: FontWeight.w400, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ), + ); + }, + ), + ), + ), + ], + ); + } + + List get availableCollections => _availableCollections; +} diff --git a/mobile/apps/locker/lib/ui/components/file_edit_dialog.dart b/mobile/apps/locker/lib/ui/components/file_edit_dialog.dart new file mode 100644 index 0000000000..0851a7acb3 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/file_edit_dialog.dart @@ -0,0 +1,234 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/text_input_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/collection_selection_widget.dart'; +import 'package:locker/utils/file_icon_utils.dart'; +import 'package:locker/utils/snack_bar_utils.dart'; + +class FileEditDialogResult { + final String title; + final String caption; + final List selectedCollections; + + FileEditDialogResult({ + required this.title, + required this.caption, + required this.selectedCollections, + }); +} + +class FileEditDialog extends StatefulWidget { + final EnteFile file; + final List collections; + + const FileEditDialog({ + super.key, + required this.file, + required this.collections, + }); + + @override + State createState() => _FileEditDialogState(); +} + +class _FileEditDialogState extends State { + final TextEditingController _titleController = TextEditingController(); + final TextEditingController _captionController = TextEditingController(); + final Set _selectedCollectionIds = {}; + List _availableCollections = []; + + @override + void initState() { + super.initState(); + + _availableCollections = List.from(widget.collections); + + _titleController.text = widget.file.displayName; + + _captionController.text = widget.file.caption ?? ''; + + CollectionService.instance + .getCollectionsForFile(widget.file) + .then((fileCollections) { + for (final collection in fileCollections) { + _selectedCollectionIds.add(collection.id); + } + setState(() {}); + }); + } + + @override + void dispose() { + _titleController.dispose(); + _captionController.dispose(); + super.dispose(); + } + + void _toggleCollection(int collectionId) { + setState(() { + if (_selectedCollectionIds.contains(collectionId)) { + _selectedCollectionIds.remove(collectionId); + } else { + _selectedCollectionIds.add(collectionId); + } + }); + } + + void _onCollectionsUpdated(List updatedCollections) { + setState(() { + _availableCollections = updatedCollections; + }); + } + + Future _onCancel() async { + Navigator.of(context).pop(); + } + + Future _onSave() async { + final selectedCollections = _availableCollections + .where((c) => _selectedCollectionIds.contains(c.id)) + .toList(); + + if (selectedCollections.isEmpty) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.pleaseSelectAtLeastOneCollection, + ); + return; + } + + final result = FileEditDialogResult( + title: _titleController.text.trim(), + caption: _captionController.text.trim(), + selectedCollections: selectedCollections, + ); + + Navigator.of(context).pop(result); + } + + String get _fileName { + return widget.file.displayName; + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return Dialog( + backgroundColor: colorScheme.backgroundElevated, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Container( + width: 400, + constraints: const BoxConstraints(maxHeight: 600), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + FileIconUtils.getFileIcon(_fileName), + color: FileIconUtils.getFileIconColor(_fileName), + size: 24, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + _fileName, + style: textTheme.largeBold, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 20), + Text( + 'Title', + style: textTheme.small.copyWith( + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 8), + TextInputWidget( + hintText: context.l10n.fileTitle, + initialValue: _titleController.text, + onChange: (value) => _titleController.text = value, + maxLength: 200, + textCapitalization: TextCapitalization.words, + ), + const SizedBox(height: 16), + Text( + context.l10n.note, + style: textTheme.small.copyWith( + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 8), + TextInputWidget( + hintText: context.l10n.optionalNote, + initialValue: _captionController.text, + onChange: (value) => _captionController.text = value, + maxLength: 500, + textCapitalization: TextCapitalization.sentences, + ), + const SizedBox(height: 16), + CollectionSelectionWidget( + collections: _availableCollections, + selectedCollectionIds: _selectedCollectionIds, + onToggleCollection: _toggleCollection, + onCollectionsUpdated: _onCollectionsUpdated, + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.l10n.cancel, + onTap: _onCancel, + ), + ), + const SizedBox(width: 12), + Flexible( + child: ButtonWidget( + buttonType: ButtonType.primary, + labelText: context.l10n.save, + onTap: _onSave, + isDisabled: _selectedCollectionIds.isEmpty, + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +Future showFileEditDialog( + BuildContext context, { + required EnteFile file, + required List collections, +}) async { + return showDialog( + context: context, + barrierColor: getEnteColorScheme(context).backdropBase, + builder: (context) => FileEditDialog( + file: file, + collections: collections, + ), + ); +} diff --git a/mobile/apps/locker/lib/ui/components/file_upload_dialog.dart b/mobile/apps/locker/lib/ui/components/file_upload_dialog.dart new file mode 100644 index 0000000000..f1bcdd38c9 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/file_upload_dialog.dart @@ -0,0 +1,208 @@ +import 'dart:io'; + +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/components/text_input_widget.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/ui/components/collection_selection_widget.dart'; +import 'package:locker/utils/file_icon_utils.dart'; +import 'package:locker/utils/snack_bar_utils.dart'; +import 'package:path/path.dart' as path; + +class FileUploadDialogResult { + final String note; + final List selectedCollections; + + FileUploadDialogResult({ + required this.note, + required this.selectedCollections, + }); +} + +class FileUploadDialog extends StatefulWidget { + final File file; + final List collections; + final Collection? selectedCollection; + + const FileUploadDialog({ + super.key, + required this.file, + required this.collections, + this.selectedCollection, + }); + + @override + State createState() => _FileUploadDialogState(); +} + +class _FileUploadDialogState extends State { + final TextEditingController _noteController = TextEditingController(); + final Set _selectedCollectionIds = {}; + List _availableCollections = []; + + @override + void initState() { + super.initState(); + _availableCollections = List.from(widget.collections); + if (widget.selectedCollection != null) { + _selectedCollectionIds.add(widget.selectedCollection!.id); + } + } + + @override + void dispose() { + _noteController.dispose(); + super.dispose(); + } + + void _toggleCollection(int collectionId) { + setState(() { + if (_selectedCollectionIds.contains(collectionId)) { + _selectedCollectionIds.remove(collectionId); + } else { + _selectedCollectionIds.add(collectionId); + } + }); + } + + void _onCollectionsUpdated(List updatedCollections) { + setState(() { + _availableCollections = updatedCollections; + }); + } + + Future _onCancel() async { + Navigator.of(context).pop(); + } + + Future _onSave() async { + final selectedCollections = _availableCollections + .where((c) => _selectedCollectionIds.contains(c.id)) + .toList(); + + if (selectedCollections.isEmpty) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.pleaseSelectAtLeastOneCollection, + ); + return; + } + + final result = FileUploadDialogResult( + note: _noteController.text.trim(), + selectedCollections: selectedCollections, + ); + + Navigator.of(context).pop(result); + } + + String get _fileName { + return path.basename(widget.file.path); + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return Dialog( + backgroundColor: colorScheme.backgroundElevated, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Container( + width: 400, + constraints: const BoxConstraints(maxHeight: 600), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + FileIconUtils.getFileIcon(_fileName), + color: FileIconUtils.getFileIconColor(_fileName), + size: 24, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + _fileName, + style: textTheme.largeBold, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 20), + CollectionSelectionWidget( + collections: _availableCollections, + selectedCollectionIds: _selectedCollectionIds, + onToggleCollection: _toggleCollection, + onCollectionsUpdated: _onCollectionsUpdated, + ), + const SizedBox(height: 16), + Text( + 'Note', + style: textTheme.small.copyWith( + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 8), + TextInputWidget( + hintText: context.l10n.optionalNote, + initialValue: _noteController.text, + onChange: (value) => _noteController.text = value, + maxLength: 500, + textCapitalization: TextCapitalization.sentences, + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.l10n.cancel, + onTap: _onCancel, + ), + ), + const SizedBox(width: 12), + Flexible( + child: ButtonWidget( + buttonType: ButtonType.primary, + labelText: context.l10n.upload, + onTap: _onSave, + isDisabled: _selectedCollectionIds.isEmpty, + ), + ), + ], + ), + ], + ), + ), + ); + } +} + +Future showFileUploadDialog( + BuildContext context, { + required File file, + required List collections, + Collection? selectedCollection, +}) async { + return showDialog( + context: context, + barrierColor: getEnteColorScheme(context).backdropBase, + builder: (context) => FileUploadDialog( + file: file, + collections: collections, + selectedCollection: selectedCollection, + ), + ); +} diff --git a/mobile/apps/locker/lib/ui/components/information_addition_dialog.dart b/mobile/apps/locker/lib/ui/components/information_addition_dialog.dart new file mode 100644 index 0000000000..63021a39a7 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/information_addition_dialog.dart @@ -0,0 +1,208 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; + +enum InformationType { + physicalDocument, + emergencyContact, + accountCredential, +} + +class InformationAdditionResult { + final InformationType type; + + InformationAdditionResult({ + required this.type, + }); +} + +class InformationAdditionDialog extends StatefulWidget { + const InformationAdditionDialog({super.key}); + + @override + State createState() => + _InformationAdditionDialogState(); +} + +class _InformationAdditionDialogState extends State { + void _onTypeSelected(InformationType type) { + final result = InformationAdditionResult(type: type); + Navigator.of(context).pop(result); + } + + Future _onCancel() async { + Navigator.of(context).pop(); + } + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return Dialog( + backgroundColor: colorScheme.backgroundElevated, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Container( + width: 400, + constraints: const BoxConstraints(maxHeight: 600), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon( + Icons.post_add, + color: Colors.blue, + size: 24, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.addInformation, + style: textTheme.largeBold, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 8), + Text( + context.l10n.addInformationDialogSubtitle, + style: textTheme.body.copyWith( + color: colorScheme.textMuted, + ), + ), + const SizedBox(height: 20), + Flexible( + child: SingleChildScrollView( + child: Column( + children: [ + _buildOptionTile( + type: InformationType.physicalDocument, + icon: Icons.description, + title: context.l10n.physicalDocument, + subtitle: context.l10n.physicalDocumentDescription, + colorScheme: colorScheme, + textTheme: textTheme, + ), + const SizedBox(height: 12), + _buildOptionTile( + type: InformationType.emergencyContact, + icon: Icons.emergency, + title: context.l10n.emergencyContact, + subtitle: context.l10n.emergencyContactDescription, + colorScheme: colorScheme, + textTheme: textTheme, + ), + const SizedBox(height: 12), + _buildOptionTile( + type: InformationType.accountCredential, + icon: Icons.key, + title: context.l10n.accountCredential, + subtitle: context.l10n.accountCredentialDescription, + colorScheme: colorScheme, + textTheme: textTheme, + ), + ], + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.l10n.cancel, + onTap: _onCancel, + ), + ), + ], + ), + ], + ), + ), + ); + } + + Widget _buildOptionTile({ + required InformationType type, + required IconData icon, + required String title, + required String subtitle, + required colorScheme, + required textTheme, + }) { + return InkWell( + onTap: () => _onTypeSelected(type), + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: colorScheme.strokeFaint, + width: 1, + ), + ), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: colorScheme.fillMuted, + borderRadius: BorderRadius.circular(6), + ), + child: Icon( + icon, + color: colorScheme.textMuted, + size: 20, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: textTheme.body.copyWith( + fontWeight: FontWeight.w600, + color: colorScheme.textBase, + ), + ), + const SizedBox(height: 4), + Text( + subtitle, + style: textTheme.small.copyWith( + color: colorScheme.textMuted, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} + +Future showInformationAdditionDialog( + BuildContext context, +) async { + return showDialog( + context: context, + barrierColor: getEnteColorScheme(context).backdropBase, + builder: (context) => const InformationAdditionDialog(), + ); +} diff --git a/mobile/apps/locker/lib/ui/components/item_list_view.dart b/mobile/apps/locker/lib/ui/components/item_list_view.dart new file mode 100644 index 0000000000..5da2fe18e2 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/item_list_view.dart @@ -0,0 +1,1296 @@ +import 'dart:io'; + +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/share_utils.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/services/files/download/file_downloader.dart'; +import 'package:locker/services/files/links/links_service.dart'; +import 'package:locker/services/files/sync/metadata_updater_service.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/file_edit_dialog.dart'; +import 'package:locker/ui/pages/collection_page.dart'; +import 'package:locker/utils/collection_actions.dart'; +import 'package:locker/utils/collection_sort_util.dart'; +import 'package:locker/utils/date_time_util.dart'; +import 'package:locker/utils/file_icon_utils.dart'; +import 'package:locker/utils/snack_bar_utils.dart'; +import 'package:open_file/open_file.dart'; + +class OverflowMenuAction { + final String id; + final String label; + final IconData icon; + final void Function( + BuildContext context, + EnteFile? file, + Collection? collection, + ) onTap; + + const OverflowMenuAction({ + required this.id, + required this.label, + required this.icon, + required this.onTap, + }); +} + +class ItemListView extends StatefulWidget { + final List files; + final List collections; + final bool enableSorting; + final Widget? emptyStateWidget; + final List? fileOverflowActions; + final List? collectionOverflowActions; + + const ItemListView({ + super.key, + this.files = const [], + this.collections = const [], + this.enableSorting = false, + this.emptyStateWidget, + this.fileOverflowActions, + this.collectionOverflowActions, + }); + + @override + State createState() => _ItemListViewState(); +} + +class _ItemListViewState extends State { + List<_ListItem> _sortedItems = []; + int _sortColumnIndex = 1; + bool _sortAscending = false; + + @override + void initState() { + super.initState(); + _updateItems(); + } + + @override + void didUpdateWidget(ItemListView oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.files != oldWidget.files || + widget.collections != oldWidget.collections) { + _updateItems(); + } + } + + void _updateItems() { + final sortedCollections = + CollectionSortUtil.getSortedCollections(widget.collections); + + _sortedItems = [ + ...sortedCollections.map((c) => _CollectionListItem(c)), + ...widget.files.map((f) => _FileListItem(f)), + ]; + + if (widget.enableSorting) { + _sortItems(_sortColumnIndex, _sortAscending); + } + } + + void _sortItems(int columnIndex, bool ascending) { + if (!widget.enableSorting) return; + + setState(() { + _sortColumnIndex = columnIndex; + _sortAscending = ascending; + + final files = _sortedItems.whereType<_FileListItem>().toList(); + final collections = + _sortedItems.whereType<_CollectionListItem>().toList(); + + switch (columnIndex) { + case 0: + files.sort((a, b) { + final nameA = a.name.toLowerCase(); + final nameB = b.name.toLowerCase(); + return ascending ? nameA.compareTo(nameB) : nameB.compareTo(nameA); + }); + collections.sort((a, b) { + return CollectionSortUtil.compareCollectionsWithFavoritesPriority( + a.collection, + b.collection, + ascending, + ); + }); + break; + case 1: + files.sort((a, b) { + final dateA = a.modificationTime; + final dateB = b.modificationTime; + return ascending ? dateA.compareTo(dateB) : dateB.compareTo(dateA); + }); + collections.sort((a, b) { + return CollectionSortUtil + .compareCollectionsByDateWithFavoritesPriority( + a.collection, + b.collection, + ascending, + ); + }); + break; + } + + _sortedItems = [...collections, ...files]; + }); + } + + @override + Widget build(BuildContext context) { + if (_sortedItems.isEmpty && widget.emptyStateWidget != null) { + return widget.emptyStateWidget!; + } + + if (_sortedItems.isEmpty) { + return _buildDefaultEmptyState(context); + } + + return Card( + margin: EdgeInsets.zero, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.enableSorting) _buildSortingHeader(context), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + itemCount: _sortedItems.length, + itemBuilder: (context, index) { + final item = _sortedItems[index]; + final isLastItem = index == _sortedItems.length - 1; + return ListItemWidget( + item: item, + collections: widget.collections, + fileOverflowActions: widget.fileOverflowActions, + collectionOverflowActions: widget.collectionOverflowActions, + isLastItem: isLastItem, + ); + }, + ), + ], + ), + ); + } + + Widget _buildDefaultEmptyState(BuildContext context) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.folder_off, + size: 64, + color: Colors.grey, + ), + const SizedBox(height: 16), + Text( + context.l10n.noFilesFound, + style: getEnteTextTheme(context).body.copyWith( + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } + + Widget _buildSortingHeader(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor, + width: 0.1, + ), + ), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + _sortItems(0, _sortColumnIndex == 0 ? !_sortAscending : true), + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Flexible( + child: Text( + context.l10n.name, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + const SizedBox(width: 8), + if (_sortColumnIndex == 0) + Icon( + _sortAscending + ? Icons.arrow_upward + : Icons.arrow_downward, + size: 16, + color: Theme.of(context).primaryColor, + ), + ], + ), + ), + ), + ), + Expanded( + flex: 1, + child: GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => + _sortItems(1, _sortColumnIndex == 1 ? !_sortAscending : true), + child: Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Row( + children: [ + Flexible( + child: Text( + context.l10n.date, + style: const TextStyle(fontWeight: FontWeight.bold), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + const SizedBox(width: 8), + if (_sortColumnIndex == 1) + Icon( + _sortAscending + ? Icons.arrow_upward + : Icons.arrow_downward, + size: 16, + color: Theme.of(context).primaryColor, + ), + ], + ), + ), + ), + ), + const SizedBox(width: 48), + ], + ), + ); + } +} + +abstract class _ListItem { + String get name; + DateTime get modificationTime; + bool get isCollection; + + Collection? get collection => null; + + EnteFile? get file => null; +} + +class _CollectionListItem extends _ListItem { + final Collection _collection; + + _CollectionListItem(this._collection); + + @override + String get name { + return _collection.name ?? 'Unnamed Collection'; + } + + @override + DateTime get modificationTime { + return DateTime.fromMicrosecondsSinceEpoch(_collection.updationTime); + } + + @override + bool get isCollection => true; + + @override + Collection get collection => _collection; +} + +class _FileListItem extends _ListItem { + final EnteFile _file; + + _FileListItem(this._file); + + @override + String get name { + return _file.displayName; + } + + @override + DateTime get modificationTime { + if (_file.updationTime != null) { + return DateTime.fromMicrosecondsSinceEpoch(_file.updationTime!); + } + if (_file.modificationTime != null) { + return DateTime.fromMillisecondsSinceEpoch(_file.modificationTime!); + } + if (_file.creationTime != null) { + return DateTime.fromMillisecondsSinceEpoch(_file.creationTime!); + } + return DateTime.now(); + } + + @override + bool get isCollection => false; + + @override + EnteFile get file => _file; +} + +class ListItemWidget extends StatelessWidget { + // ignore: library_private_types_in_public_api + final _ListItem item; + final List collections; + final List? fileOverflowActions; + final List? collectionOverflowActions; + final bool isLastItem; + + const ListItemWidget({ + super.key, + // ignore: library_private_types_in_public_api + required this.item, + required this.collections, + this.fileOverflowActions, + this.collectionOverflowActions, + this.isLastItem = false, + }); + + @override + Widget build(BuildContext context) { + if (item.isCollection && item.collection != null) { + return CollectionRowWidget( + collection: item.collection!, + overflowActions: collectionOverflowActions, + isLastItem: isLastItem, + ); + } else if (!item.isCollection && item.file != null) { + return FileRowWidget( + file: item.file!, + collections: collections, + overflowActions: fileOverflowActions, + isLastItem: isLastItem, + ); + } else { + return Container( + padding: const EdgeInsets.all(16), + child: Text(context.l10n.unknownItemType), + ); + } + } +} + +class CollectionRowWidget extends StatelessWidget { + final Collection collection; + final List? overflowActions; + final bool isLastItem; + + const CollectionRowWidget({ + super.key, + required this.collection, + this.overflowActions, + this.isLastItem = false, + }); + + @override + Widget build(BuildContext context) { + final updateTime = + DateTime.fromMicrosecondsSinceEpoch(collection.updationTime); + + return InkWell( + onTap: () => _openCollection(context), + child: Container( + padding: EdgeInsets.fromLTRB(16.0, 2, 16.0, isLastItem ? 8 : 2), + decoration: BoxDecoration( + border: isLastItem + ? null + : Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor.withOpacity(0.3), + width: 0.5, + ), + ), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + Icons.folder_open, + color: collection.type == CollectionType.favorites + ? getEnteColorScheme(context).primary500 + : Colors.grey, + size: 20, + ), + const SizedBox(width: 12), + Flexible( + child: Text( + collection.name ?? 'Unnamed Collection', + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: getEnteTextTheme(context).body, + ), + ), + ], + ), + ], + ), + ), + Expanded( + flex: 1, + child: Text( + formatDate(context, updateTime), + style: getEnteTextTheme(context).small.copyWith( + color: Theme.of(context).textTheme.bodySmall?.color, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + PopupMenuButton( + onSelected: (value) => _handleMenuAction(context, value), + icon: const Icon( + Icons.more_vert, + size: 20, + ), + itemBuilder: (BuildContext context) { + if (overflowActions != null && overflowActions!.isNotEmpty) { + return overflowActions! + .map( + (action) => PopupMenuItem( + value: action.id, + child: Row( + children: [ + Icon(action.icon, size: 16), + const SizedBox(width: 8), + Text(action.label), + ], + ), + ), + ) + .toList(); + } else { + return [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Icons.edit, size: 16), + const SizedBox(width: 8), + Text(context.l10n.edit), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + const Icon(Icons.delete, size: 16), + const SizedBox(width: 8), + Text(context.l10n.delete), + ], + ), + ), + ]; + } + }, + ), + ], + ), + ), + ); + } + + void _handleMenuAction(BuildContext context, String action) { + if (overflowActions != null && overflowActions!.isNotEmpty) { + final customAction = overflowActions!.firstWhere( + (a) => a.id == action, + orElse: () => throw StateError('Action not found'), + ); + customAction.onTap(context, null, collection); + } else { + switch (action) { + case 'edit': + _editCollection(context); + break; + case 'delete': + _deleteCollection(context); + break; + } + } + } + + void _editCollection(BuildContext context) { + CollectionActions.editCollection(context, collection); + } + + void _deleteCollection(BuildContext context) { + CollectionActions.deleteCollection(context, collection); + } + + void _openCollection(BuildContext context) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => CollectionPage(collection: collection), + ), + ); + } +} + +class FileRowWidget extends StatelessWidget { + final EnteFile file; + final List collections; + final List? overflowActions; + final bool isLastItem; + + const FileRowWidget({ + super.key, + required this.file, + required this.collections, + this.overflowActions, + this.isLastItem = false, + }); + + @override + Widget build(BuildContext context) { + final updateTime = file.updationTime != null + ? DateTime.fromMicrosecondsSinceEpoch(file.updationTime!) + : (file.modificationTime != null + ? DateTime.fromMillisecondsSinceEpoch(file.modificationTime!) + : (file.creationTime != null + ? DateTime.fromMillisecondsSinceEpoch(file.creationTime!) + : DateTime.now())); + + return InkWell( + onTap: () => _openFile(context), + child: Container( + padding: EdgeInsets.fromLTRB(16.0, 2, 16.0, isLastItem ? 8 : 2), + decoration: BoxDecoration( + border: isLastItem + ? null + : Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor.withOpacity(0.3), + width: 0.5, + ), + ), + ), + child: Row( + children: [ + Expanded( + flex: 2, + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Icon( + FileIconUtils.getFileIcon(file.displayName), + color: + FileIconUtils.getFileIconColor(file.displayName), + size: 20, + ), + const SizedBox(width: 12), + Flexible( + child: Text( + file.displayName, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: getEnteTextTheme(context).body, + ), + ), + ], + ), + ], + ), + ), + ), + Expanded( + flex: 1, + child: Text( + formatDate(context, updateTime), + style: getEnteTextTheme(context).small.copyWith( + color: Theme.of(context).textTheme.bodySmall?.color, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + PopupMenuButton( + onSelected: (value) => _handleMenuAction(context, value), + icon: const Icon( + Icons.more_vert, + size: 20, + ), + itemBuilder: (BuildContext context) { + if (overflowActions != null && overflowActions!.isNotEmpty) { + return overflowActions! + .map( + (action) => PopupMenuItem( + value: action.id, + child: Row( + children: [ + Icon(action.icon, size: 16), + const SizedBox(width: 8), + Text(action.label), + ], + ), + ), + ) + .toList(); + } else { + return [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Icons.edit, size: 16), + const SizedBox(width: 8), + Text(context.l10n.edit), + ], + ), + ), + PopupMenuItem( + value: 'share_link', + child: Row( + children: [ + const Icon(Icons.share, size: 16), + const SizedBox(width: 8), + Text(context.l10n.share), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + const Icon(Icons.delete, size: 16), + const SizedBox(width: 8), + Text(context.l10n.delete), + ], + ), + ), + ]; + } + }, + ), + ], + ), + ), + ); + } + + void _handleMenuAction(BuildContext context, String action) { + if (overflowActions != null && overflowActions!.isNotEmpty) { + final customAction = overflowActions!.firstWhere( + (a) => a.id == action, + orElse: () => throw StateError('Action not found'), + ); + customAction.onTap(context, file, null); + } else { + switch (action) { + case 'edit': + _showEditDialog(context); + break; + case 'share_link': + _shareLink(context); + break; + case 'delete': + _showDeleteConfirmationDialog(context); + break; + } + } + } + + Future _shareLink(BuildContext context) async { + final dialog = createProgressDialog( + context, + context.l10n.creatingShareLink, + isDismissible: false, + ); + + try { + await dialog.show(); + + // Get or create the share link + final shareableLink = await LinksService.instance.getOrCreateLink(file); + + await dialog.hide(); + + // Show the link dialog with copy and delete options + if (context.mounted) { + await _showShareLinkDialog( + context, + shareableLink.fullURL!, + shareableLink.linkID, + ); + } + } catch (e) { + await dialog.hide(); + + if (context.mounted) { + SnackBarUtils.showWarningSnackBar( + context, + '${context.l10n.failedToCreateShareLink}: ${e.toString()}', + ); + } + } + } + + Future _showShareLinkDialog( + BuildContext context, + String url, + String linkID, + ) async { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + // Capture the root context (with Scaffold) before showing dialog + final rootContext = context; + + await showDialog( + context: context, + builder: (BuildContext dialogContext) { + return StatefulBuilder( + builder: (context, setState) { + return AlertDialog( + title: Text( + dialogContext.l10n.share, + style: textTheme.largeBold, + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + dialogContext.l10n.shareThisLink, + style: textTheme.body, + ), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.strokeFaint), + ), + child: Row( + children: [ + Expanded( + child: SelectableText( + url, + style: textTheme.small, + ), + ), + const SizedBox(width: 8), + _CopyButton( + url: url, + ), + ], + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () async { + Navigator.of(context).pop(); + await _deleteShareLink(rootContext, file.uploadedFileID!); + }, + child: Text( + dialogContext.l10n.deleteLink, + style: + textTheme.body.copyWith(color: colorScheme.warning500), + ), + ), + TextButton( + onPressed: () async { + Navigator.of(dialogContext).pop(); + // Use system share sheet to share the URL + await shareText( + url, + context: rootContext, + ); + }, + child: Text( + dialogContext.l10n.shareLink, + style: + textTheme.body.copyWith(color: colorScheme.primary500), + ), + ), + ], + ); + }, + ); + }, + ); + } + + Future _deleteShareLink(BuildContext context, int fileID) async { + final result = await showChoiceDialog( + context, + title: context.l10n.deleteShareLinkDialogTitle, + body: context.l10n.deleteShareLinkConfirmation, + firstButtonLabel: context.l10n.delete, + secondButtonLabel: context.l10n.cancel, + firstButtonType: ButtonType.critical, + isCritical: true, + ); + if (result?.action == ButtonAction.first && context.mounted) { + final dialog = createProgressDialog( + context, + context.l10n.deletingShareLink, + isDismissible: false, + ); + + try { + await dialog.show(); + await LinksService.instance.deleteLink(fileID); + await dialog.hide(); + + if (context.mounted) { + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.shareLinkDeletedSuccessfully, + ); + } + } catch (e) { + await dialog.hide(); + + if (context.mounted) { + SnackBarUtils.showWarningSnackBar( + context, + '${context.l10n.failedToDeleteShareLink}: ${e.toString()}', + ); + } + } + } + } + + Future _showDeleteConfirmationDialog(BuildContext context) async { + final result = await showChoiceDialog( + context, + title: context.l10n.deleteFile, + body: context.l10n.deleteFileConfirmation(file.displayName), + firstButtonLabel: context.l10n.delete, + secondButtonLabel: context.l10n.cancel, + firstButtonType: ButtonType.critical, + isCritical: true, + ); + + if (result?.action == ButtonAction.first && context.mounted) { + await _deleteFile(context); + } + } + + Future _deleteFile(BuildContext context) async { + final dialog = createProgressDialog( + context, + context.l10n.deletingFile, + isDismissible: false, + ); + + try { + await dialog.show(); + + final collections = + await CollectionService.instance.getCollectionsForFile(file); + if (collections.isNotEmpty) { + await CollectionService.instance.trashFile(file, collections.first); + } + + await dialog.hide(); + + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.fileDeletedSuccessfully, + ); + } catch (e) { + await dialog.hide(); + + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToDeleteFile(e.toString()), + ); + } + } + + Future _showEditDialog(BuildContext context) async { + final allCollections = await CollectionService.instance.getCollections(); + allCollections.removeWhere( + (c) => c.type == CollectionType.uncategorized, + ); + + final result = await showFileEditDialog( + context, + file: file, + collections: allCollections, + ); + + if (result != null && context.mounted) { + List currentCollections; + try { + currentCollections = + await CollectionService.instance.getCollectionsForFile(file); + } catch (e) { + currentCollections = []; + } + + final currentCollectionsSet = currentCollections.toSet(); + + final newCollectionsSet = result.selectedCollections.toSet(); + + final collectionsToAdd = + newCollectionsSet.difference(currentCollectionsSet).toList(); + + final collectionsToRemove = + currentCollectionsSet.difference(newCollectionsSet).toList(); + + final currentTitle = file.displayName; + final currentCaption = file.caption ?? ''; + final hasMetadataChanged = + result.title != currentTitle || result.caption != currentCaption; + + if (hasMetadataChanged || currentCollectionsSet != newCollectionsSet) { + final dialog = createProgressDialog( + context, + context.l10n.pleaseWait, + isDismissible: false, + ); + await dialog.show(); + + try { + final List> apiCalls = []; + for (final collection in collectionsToAdd) { + apiCalls.add( + CollectionService.instance.addToCollection(collection, file), + ); + } + await Future.wait(apiCalls); + apiCalls.clear(); + + for (final collection in collectionsToRemove) { + apiCalls.add( + CollectionService.instance + .move(file, collection, newCollectionsSet.first), + ); + } + if (hasMetadataChanged) { + apiCalls.add( + MetadataUpdaterService.instance + .editFileNameAndCaption(file, result.title, result.caption), + ); + } + await Future.wait(apiCalls); + + await dialog.hide(); + + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.fileUpdatedSuccessfully, + ); + } catch (e) { + await dialog.hide(); + + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToUpdateFile(e.toString()), + ); + } + } else { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.noChangesWereMade, + ); + } + } + } + + Future _openFile(BuildContext context) async { + if (file.localPath != null) { + final localFile = File(file.localPath!); + if (await localFile.exists()) { + await _launchFile(context, localFile, file.displayName); + return; + } + } + + final String cachedFilePath = + "${Configuration.instance.getCacheDirectory()}${file.displayName}"; + final File cachedFile = File(cachedFilePath); + if (await cachedFile.exists()) { + await _launchFile(context, cachedFile, file.displayName); + return; + } + + final dialog = createProgressDialog( + context, + context.l10n.downloading, + isDismissible: false, + ); + + try { + await dialog.show(); + final fileKey = await CollectionService.instance.getFileKey(file); + final decryptedFile = await downloadAndDecrypt( + file, + fileKey, + progressCallback: (downloaded, total) { + if (total > 0 && downloaded >= 0) { + final percentage = + ((downloaded / total) * 100).clamp(0, 100).round(); + dialog.update( + message: context.l10n.downloadingProgress(percentage), + ); + } else { + dialog.update(message: context.l10n.downloading); + } + }, + shouldUseCache: true, + ); + + await dialog.hide(); + + if (decryptedFile != null) { + await _launchFile(context, decryptedFile, file.displayName); + } else { + await showErrorDialog( + context, + context.l10n.downloadFailed, + context.l10n.failedToDownloadOrDecrypt, + ); + } + } catch (e) { + await dialog.hide(); + await showErrorDialog( + context, + context.l10n.errorOpeningFile, + context.l10n.errorOpeningFileMessage(e.toString()), + ); + } + } + + Future _launchFile( + BuildContext context, + File file, + String fileName, + ) async { + try { + await OpenFile.open(file.path); + } catch (e) { + await showErrorDialog( + context, + context.l10n.errorOpeningFile, + context.l10n.couldNotOpenFile(e.toString()), + ); + } + } +} + +class _CopyButton extends StatefulWidget { + final String url; + + const _CopyButton({ + required this.url, + }); + + @override + State<_CopyButton> createState() => _CopyButtonState(); +} + +class _CopyButtonState extends State<_CopyButton> { + bool _isCopied = false; + + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + + return IconButton( + onPressed: () async { + await Clipboard.setData(ClipboardData(text: widget.url)); + setState(() { + _isCopied = true; + }); + // Reset the state after 2 seconds + Future.delayed(const Duration(seconds: 2), () { + if (mounted) { + setState(() { + _isCopied = false; + }); + } + }); + }, + icon: Icon( + _isCopied ? Icons.check : Icons.copy, + size: 16, + color: _isCopied ? colorScheme.primary500 : colorScheme.primary500, + ), + iconSize: 16, + constraints: const BoxConstraints(), + padding: const EdgeInsets.all(4), + tooltip: _isCopied + ? context.l10n.linkCopiedToClipboard + : context.l10n.copyLink, + ); + } +} + +class FileListViewHelpers { + static Widget createSearchEmptyState({ + required String searchQuery, + String? message, + }) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.search_off, + size: 64, + color: Colors.grey, + ), + const SizedBox(height: 16), + Text( + message ?? 'No results found for "$searchQuery"', + style: const TextStyle( + color: Colors.grey, + fontSize: 16, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 8), + const Text( + 'Try adjusting your search query', + style: TextStyle( + color: Colors.grey, + fontSize: 14, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } + + static Widget createSearchEverywhereFooter({ + required String searchQuery, + required VoidCallback onTap, + BuildContext? context, + }) { + return Container( + margin: const EdgeInsets.all(16.0), + child: Card( + elevation: 2, + child: InkWell( + onTap: onTap, + borderRadius: BorderRadius.circular(8.0), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Icon( + Icons.search, + color: context != null + ? Theme.of(context).primaryColor + : Colors.blue, + size: 24, + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Search everywhere for "$searchQuery"', + style: context != null + ? getEnteTextTheme(context).large.copyWith( + fontWeight: FontWeight.w500, + color: Theme.of(context).primaryColor, + ) + : const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + color: Colors.blue, + ), + ), + const SizedBox(height: 4), + Text( + 'Search across all collections and files', + style: context != null + ? getEnteTextTheme(context).body.copyWith( + color: Colors.grey[600], + ) + : TextStyle( + fontSize: 14, + color: Colors.grey[600], + ), + ), + ], + ), + ), + Icon( + Icons.arrow_forward_ios, + color: Colors.grey[400], + size: 16, + ), + ], + ), + ), + ), + ), + ); + } +} + +class FileDataTable extends StatelessWidget { + final List files; + final Function(EnteFile)? onFileTap; + final bool enableSorting; + + const FileDataTable({ + super.key, + required this.files, + this.onFileTap, + this.enableSorting = false, + }); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + child: const Text( + 'FileDataTable is deprecated. Use FileListView instead.', + style: TextStyle(color: Colors.red), + ), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/components/recents_section_widget.dart b/mobile/apps/locker/lib/ui/components/recents_section_widget.dart new file mode 100644 index 0000000000..ed30e5fbf8 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/recents_section_widget.dart @@ -0,0 +1,462 @@ +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/item_list_view.dart'; + +class RecentsSectionWidget extends StatefulWidget { + final List collections; + final List recentFiles; + + const RecentsSectionWidget({ + super.key, + required this.collections, + required this.recentFiles, + }); + + @override + State createState() => _RecentsSectionWidgetState(); +} + +class _RecentsSectionWidgetState extends State { + final Set _selectedCollections = {}; + final List _selectionOrder = []; + List _filteredFilesByCollections = []; + List _availableCollections = []; + late List _originalCollectionOrder; + + @override + void initState() { + super.initState(); + _originalCollectionOrder = List.from(widget.collections); + _availableCollections = List.from(widget.collections); + _updateFilteredFilesByCollections(); + _updateAvailableCollections(); + } + + @override + void didUpdateWidget(RecentsSectionWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.recentFiles != widget.recentFiles || + oldWidget.collections != widget.collections) { + _originalCollectionOrder = List.from(widget.collections); + _updateFilteredFilesByCollections(); + _updateAvailableCollections(); + } + } + + List get _displayedFiles { + if (_selectedCollections.isNotEmpty) { + return _filteredFilesByCollections; + } else { + return widget.recentFiles; + } + } + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildRecentsHeader(), + const SizedBox(height: 12), + if (widget.collections.isNotEmpty) ...[ + _buildCollectionChips(), + const SizedBox(height: 16), + ], + _buildRecentsTable(), + ], + ); + } + + Widget _buildRecentsHeader() { + return Text( + 'Recents', + style: getEnteTextTheme(context).h3Bold, + ); + } + + Widget _buildCollectionChips() { + final orderedCollections = _getOrderedCollections(); + + if (orderedCollections.isEmpty) { + return SizedBox( + height: 40, + child: Center( + child: Text( + 'No collections to filter by', + style: TextStyle( + color: Colors.grey[600], + fontSize: 14, + ), + ), + ), + ); + } + + return SizedBox( + height: 40, + child: Row( + children: [ + Expanded( + child: AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + child: ListView.builder( + key: ValueKey(orderedCollections.map((c) => c.id).join('-')), + scrollDirection: Axis.horizontal, + itemCount: orderedCollections.length, + itemBuilder: (context, index) { + final collection = orderedCollections[index]; + final isSelected = _selectedCollections.contains(collection); + + return AnimatedContainer( + duration: const Duration(milliseconds: 200), + margin: const EdgeInsets.only(right: 8), + child: _buildCollectionChip(collection, isSelected), + ); + }, + ), + ), + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + transitionBuilder: (Widget child, Animation animation) { + return SlideTransition( + position: Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: animation, + curve: Curves.easeOut, + ), + ), + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + child: _selectedCollections.isNotEmpty + ? Container( + key: const ValueKey('clear_button'), + margin: const EdgeInsets.only(left: 8), + child: _buildClearAllButton(), + ) + : const SizedBox.shrink(key: ValueKey('no_clear_button')), + ), + ], + ), + ); + } + + Widget _buildCollectionChip(Collection collection, bool isSelected) { + return AnimatedSize( + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + alignment: Alignment.centerLeft, + child: GestureDetector( + onTap: () => _onCollectionSelected(collection), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + decoration: BoxDecoration( + color: isSelected ? getEnteColorScheme(context).fillMuted : null, + border: Border.all( + color: isSelected + ? getEnteColorScheme(context).strokeBase + : getEnteColorScheme(context).fillFaint, + width: 1, + ), + borderRadius: const BorderRadius.all(Radius.circular(20.0)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + collection.name ?? 'Untitled', + style: getEnteTextTheme(context).mini, + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + transitionBuilder: (Widget child, Animation animation) { + return SlideTransition( + position: Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate( + CurvedAnimation( + parent: animation, + curve: Curves.easeOut, + ), + ), + child: FadeTransition( + opacity: animation, + child: child, + ), + ); + }, + child: isSelected + ? Row( + key: const ValueKey('close_button'), + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox(width: 8), + GestureDetector( + onTap: () => _onCollectionSelected(collection), + child: Container( + padding: const EdgeInsets.all(1), + decoration: BoxDecoration( + color: getEnteColorScheme(context).strokeBase, + shape: BoxShape.circle, + ), + child: Icon( + Icons.close, + size: 10, + color: getEnteColorScheme(context).backdropBase, + ), + ), + ), + ], + ) + : const SizedBox.shrink(key: ValueKey('no_button')), + ), + ], + ), + ), + ), + ); + } + + Widget _buildClearAllButton() { + return GestureDetector( + onTap: _clearAllSelections, + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: getEnteColorScheme(context).fillFaint, + border: Border.all( + color: getEnteColorScheme(context).strokeMuted, + width: 1, + ), + borderRadius: const BorderRadius.all(Radius.circular(16.0)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + Icons.clear_all, + size: 14, + color: getEnteColorScheme(context).textMuted, + ), + const SizedBox(width: 4), + Text( + 'Clear', + style: getEnteTextTheme(context).miniMuted, + ), + ], + ), + ), + ); + } + + Widget _buildRecentsTable() { + if (_displayedFiles.isEmpty) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 40), + child: Center( + child: Column( + children: [ + Icon( + Icons.folder_off, + size: 48, + color: Colors.grey[400], + ), + const SizedBox(height: 16), + Text( + 'No common items in selected collections', + style: getEnteTextTheme(context).body.copyWith( + color: Colors.grey[600], + ), + ), + ], + ), + ), + ); + } + + return ItemListView( + files: _displayedFiles, + enableSorting: true, + ); + } + + void _onCollectionSelected(Collection collection) { + HapticFeedback.lightImpact(); + + setState(() { + if (_selectedCollections.contains(collection)) { + _selectedCollections.remove(collection); + _selectionOrder.remove(collection); + } else { + _selectedCollections.add(collection); + _selectionOrder.add(collection); + } + }); + + _updateFilteredFilesByCollections(); + _updateAvailableCollections(); + } + + void _clearAllSelections() { + HapticFeedback.lightImpact(); + + setState(() { + _selectedCollections.clear(); + _selectionOrder.clear(); + }); + + _updateFilteredFilesByCollections(); + _updateAvailableCollections(); + } + + Future _updateAvailableCollections() async { + try { + final collectionsWithCommonFiles = await _getCollectionsWithCommonFiles( + _selectedCollections.toList(), + widget.collections, + ); + + if (mounted) { + setState(() { + _availableCollections = collectionsWithCommonFiles; + }); + } + } catch (e) { + if (mounted) { + setState(() { + _availableCollections = List.from(widget.collections); + }); + } + } + } + + Future _updateFilteredFilesByCollections() async { + if (_selectedCollections.isEmpty) { + _filteredFilesByCollections = []; + return; + } + + final filteredFiles = []; + for (final file in widget.recentFiles) { + try { + final fileCollections = + await CollectionService.instance.getCollectionsForFile(file); + final hasAllSelectedCollections = _selectedCollections.every( + (selectedCollection) => fileCollections.contains(selectedCollection), + ); + if (hasAllSelectedCollections) { + filteredFiles.add(file); + } + } catch (e) { + continue; + } + } + + if (mounted) { + setState(() { + _filteredFilesByCollections = filteredFiles; + }); + } + } + + Future> _getCollectionsWithCommonFiles( + List selectedCollections, + List allCollections, + ) async { + if (selectedCollections.isEmpty) { + return allCollections; + } + + if (selectedCollections.length == allCollections.length) { + return allCollections; + } + + try { + final Map> collectionFileCache = {}; + + Future> getCollectionFileIds(Collection collection) async { + if (collectionFileCache.containsKey(collection.id)) { + return collectionFileCache[collection.id]!; + } + + final files = + await CollectionService.instance.getFilesInCollection(collection); + final fileIds = files + .where((file) => file.uploadedFileID != null) + .map((file) => file.uploadedFileID!) + .toSet(); + + collectionFileCache[collection.id] = fileIds; + return fileIds; + } + + final selectedFileIdSets = await Future.wait( + selectedCollections.map(getCollectionFileIds), + ); + + if (selectedFileIdSets.any((set) => set.isEmpty)) { + return selectedCollections; + } + + final commonFileIds = + selectedFileIdSets.reduce((a, b) => a.intersection(b)); + + if (commonFileIds.isEmpty) { + return selectedCollections; + } + + final result = []; + + for (final collection in allCollections) { + if (selectedCollections + .any((selected) => selected.id == collection.id)) { + result.add(collection); + } else { + final collectionFileIds = await getCollectionFileIds(collection); + if (commonFileIds.any(collectionFileIds.contains)) { + result.add(collection); + } + } + } + + return result; + } catch (e) { + return allCollections; + } + } + + List _getOrderedCollections() { + final orderedCollections = []; + + for (final collection in _selectionOrder) { + if (_availableCollections.contains(collection)) { + orderedCollections.add(collection); + } + } + + for (final collection in _originalCollectionOrder) { + if (_availableCollections.contains(collection) && + !_selectedCollections.contains(collection)) { + orderedCollections.add(collection); + } + } + + return orderedCollections; + } +} diff --git a/mobile/apps/locker/lib/ui/components/search_result_view.dart b/mobile/apps/locker/lib/ui/components/search_result_view.dart new file mode 100644 index 0000000000..6096495a57 --- /dev/null +++ b/mobile/apps/locker/lib/ui/components/search_result_view.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/item_list_view.dart'; + +class SearchResultView extends StatelessWidget { + final List collections; + final List files; + final String searchQuery; + final bool enableSorting; + final VoidCallback? onCollectionTap; + final bool isHomePage; + final VoidCallback? onSearchEverywhere; + final bool showCollections; + + const SearchResultView({ + super.key, + required this.collections, + required this.files, + this.searchQuery = '', + this.enableSorting = false, + this.onCollectionTap, + this.isHomePage = false, + this.onSearchEverywhere, + this.showCollections = true, + }); + + @override + Widget build(BuildContext context) { + final displayCollections = showCollections ? collections : []; + + // For non-home pages, show search everywhere option + return Padding( + padding: const EdgeInsets.only(left: 16, right: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ItemListView( + files: files, + collections: displayCollections, + enableSorting: enableSorting, + emptyStateWidget: searchQuery.isNotEmpty + ? FileListViewHelpers.createSearchEmptyState( + searchQuery: searchQuery, + ) + : null, + ), + if (!isHomePage && + onSearchEverywhere != null && + searchQuery.isNotEmpty) + FileListViewHelpers.createSearchEverywhereFooter( + searchQuery: searchQuery, + onTap: onSearchEverywhere!, + context: context, + ), + ], + ), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/mixins/search_mixin.dart b/mobile/apps/locker/lib/ui/mixins/search_mixin.dart new file mode 100644 index 0000000000..e52437cf94 --- /dev/null +++ b/mobile/apps/locker/lib/ui/mixins/search_mixin.dart @@ -0,0 +1,348 @@ +import 'dart:async'; + +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; + +class CollectionSearchResult { + final bool matches; + final bool nameMatches; + final List files; + + CollectionSearchResult({ + required this.matches, + required this.nameMatches, + required this.files, + }); +} + +mixin SearchMixin on State { + String _searchQuery = ''; + bool _isSearchActive = false; + bool _isSearching = false; + Timer? _searchDebounceTimer; + final TextEditingController _searchController = TextEditingController(); + final FocusNode _searchFocusNode = FocusNode(); + + List get allCollections; + List get allFiles; + + void onSearchResultsChanged( + List collections, + List files, + ); + + void onSearchStateChanged(bool isActive) {} + + @override + void dispose() { + _searchController.dispose(); + _searchFocusNode.dispose(); + _searchDebounceTimer?.cancel(); + super.dispose(); + } + + Widget buildSearchAction() { + if (_isSearchActive) { + return Flexible( + child: Container( + margin: const EdgeInsets.fromLTRB(8.0, 8.0, 8.0, 8.0), + constraints: const BoxConstraints( + minWidth: 200, + maxWidth: double.infinity, + ), + child: TextField( + controller: _searchController, + focusNode: _searchFocusNode, + autofocus: true, + onChanged: _onSearchChanged, + decoration: InputDecoration( + hintText: context.l10n.searchHint, + hintStyle: TextStyle( + color: Theme.of(context) + .textTheme + .bodyMedium + ?.color + ?.withOpacity(0.6), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(20.0), + borderSide: BorderSide.none, + ), + filled: true, + fillColor: getEnteColorScheme(context).backdropBase, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 8.0, + ), + prefixIcon: _isSearching + ? Container( + width: 20, + height: 20, + padding: const EdgeInsets.all(8.0), + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: AlwaysStoppedAnimation( + Theme.of(context) + .textTheme + .bodyMedium + ?.color + ?.withOpacity(0.6) ?? + Colors.grey, + ), + ), + ) + : Icon( + Icons.search, + size: 20, + color: Theme.of(context) + .textTheme + .bodyMedium + ?.color + ?.withOpacity(0.6), + ), + ), + style: TextStyle( + fontSize: 14, + color: Theme.of(context).textTheme.bodyMedium?.color, + ), + ), + ), + ); + } else { + return IconButton( + icon: const Icon(Icons.search), + onPressed: _activateSearch, + ); + } + } + + List buildSearchActions() { + if (_isSearchActive) { + return [ + IconButton( + icon: const Icon(Icons.close), + onPressed: _deactivateSearch, + ), + ]; + } + return []; + } + + Widget? buildSearchLeading({Widget? defaultLeading}) { + if (_isSearchActive) { + return null; + } + return defaultLeading; + } + + void _activateSearch() { + setState(() { + _isSearchActive = true; + }); + onSearchStateChanged(true); + WidgetsBinding.instance.addPostFrameCallback((_) { + _searchFocusNode.requestFocus(); + }); + } + + void _deactivateSearch() { + setState(() { + _isSearchActive = false; + _searchQuery = ''; + }); + _searchController.clear(); + onSearchStateChanged(false); + unawaited(_performSearch('')); + } + + void _clearSearch() { + _searchController.clear(); + _onSearchChanged(''); + } + + void _onSearchChanged(String query) { + _searchDebounceTimer?.cancel(); + + if (query.isEmpty && _searchQuery.isNotEmpty) { + setState(() { + _searchQuery = query; + }); + unawaited(_performSearch(query)); + return; + } + + _searchDebounceTimer = Timer(const Duration(milliseconds: 300), () { + if (mounted) { + setState(() { + _searchQuery = query; + }); + unawaited(_performSearch(query)); + } + }); + } + + Future _performSearch(String query) async { + if (!mounted) return; + + setState(() { + _isSearching = true; + }); + + try { + if (query.isEmpty) { + if (mounted) { + onSearchResultsChanged(allCollections, allFiles); + } + return; + } + + final List filteredCollections = []; + final List collectionFiles = []; + + for (final collection in allCollections) { + if (!mounted) return; + final searchResult = await _searchInCollection(collection, query); + if (searchResult.matches) { + filteredCollections.add(collection); + if (searchResult.nameMatches) { + collectionFiles.addAll(searchResult.files); + } + } + } + + final Set addedFileIds = + collectionFiles.map((f) => f.uploadedFileID.toString()).toSet(); + final filteredFiles = allFiles.where((file) { + if (addedFileIds.contains(file.uploadedFileID.toString())) { + return false; + } + return _searchInFile(file, query); + }).toList(); + + final List allFilteredFiles = [ + ...collectionFiles, + ...filteredFiles, + ]; + + if (mounted) { + onSearchResultsChanged(filteredCollections, allFilteredFiles); + } + } catch (e) { + debugPrint('Search error: $e'); + if (mounted) { + onSearchResultsChanged(allCollections, allFiles); + } + } finally { + if (mounted) { + setState(() { + _isSearching = false; + }); + } + } + } + + Future _searchInCollection( + Collection collection, + String query, + ) async { + try { + final files = + await CollectionService.instance.getFilesInCollection(collection); + final collectionNameMatches = _containsQuery( + collection.name ?? '', + query, + ); + final fileMatches = files.any((file) => _searchInFile(file, query)); + + return CollectionSearchResult( + matches: collectionNameMatches || fileMatches, + nameMatches: collectionNameMatches, + files: collectionNameMatches ? files : [], + ); + } catch (e) { + debugPrint('Error searching in collection ${collection.name}: $e'); + final collectionNameMatches = _containsQuery( + collection.name ?? '', + query, + ); + return CollectionSearchResult( + matches: collectionNameMatches, + nameMatches: collectionNameMatches, + files: [], + ); + } + } + + bool _searchInFile(EnteFile file, String query) { + return _containsQuery(file.displayName, query) || + _containsQuery(file.title ?? '', query) || + _containsQuery(file.caption ?? '', query) || + _containsQuery(file.pubMagicMetadata.editedName ?? '', query) || + _containsQuery(file.pubMagicMetadata.uploaderName ?? '', query); + } + + bool _containsQuery(String text, String query) { + if (text.isEmpty) return false; + final lowerText = text.toLowerCase(); + final lowerQuery = query.toLowerCase(); + + if (lowerText.contains(lowerQuery)) { + return true; + } + final words = lowerText.split(RegExp(r'[\s\-_\.]+')); + return words.any((word) => word.startsWith(lowerQuery)); + } + + /// Handle keyboard shortcuts + bool handleKeyEvent(KeyEvent event) { + if (event is KeyDownEvent) { + // Clear search or close search on ESC + if (event.logicalKey == LogicalKeyboardKey.escape) { + if (_isSearchActive) { + if (_searchQuery.isNotEmpty) { + _clearSearch(); + } else { + _deactivateSearch(); + } + return true; + } + } + // Activate search on Ctrl+F (Cmd+F on Mac) + else if (event.logicalKey == LogicalKeyboardKey.keyF && + (HardwareKeyboard.instance.isMetaPressed || + HardwareKeyboard.instance.isControlPressed)) { + if (!_isSearchActive) { + _activateSearch(); + return true; + } + } + } + return false; + } + + String get searchQuery => _searchQuery; + bool get isSearchActive => _isSearchActive; + bool get isSearching => _isSearching; + TextEditingController get searchController => _searchController; + + /// Programmatically activate search with a specific query + void activateSearchWithQuery(String query) { + setState(() { + _isSearchActive = true; + _searchQuery = query; + }); + _searchController.text = query; + onSearchStateChanged(true); + unawaited(_performSearch(query)); + + // Focus the search field after the widget is built + WidgetsBinding.instance.addPostFrameCallback((_) { + _searchFocusNode.requestFocus(); + }); + } +} diff --git a/mobile/apps/locker/lib/ui/pages/all_collections_page.dart b/mobile/apps/locker/lib/ui/pages/all_collections_page.dart new file mode 100644 index 0000000000..f225f4f0e2 --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/all_collections_page.dart @@ -0,0 +1,408 @@ +import 'dart:async'; + +import 'package:ente_events/event_bus.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/events/collections_updated_event.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/trash/trash_service.dart'; +import 'package:locker/ui/components/item_list_view.dart'; +import 'package:locker/ui/components/search_result_view.dart'; +import 'package:locker/ui/mixins/search_mixin.dart'; +import 'package:locker/ui/pages/collection_page.dart'; +import 'package:locker/ui/pages/home_page.dart'; +import 'package:locker/ui/pages/trash_page.dart'; +import 'package:locker/utils/collection_sort_util.dart'; +import 'package:logging/logging.dart'; + +class AllCollectionsPage extends StatefulWidget { + const AllCollectionsPage({super.key}); + + @override + State createState() => _AllCollectionsPageState(); +} + +class _AllCollectionsPageState extends State + with SearchMixin { + List _sortedCollections = []; + List _allCollections = []; + Collection? _uncategorizedCollection; + int? _uncategorizedFileCount; + List _allFiles = []; + bool _isLoading = true; + String? _error; + final _logger = Logger("AllCollectionsPage"); + + @override + List get allCollections => _allCollections; + + @override + List get allFiles => _allFiles; + + @override + void onSearchResultsChanged( + List collections, + List files, + ) { + setState(() { + if (searchQuery.isEmpty) { + final regularCollections = collections + .where((c) => c.type != CollectionType.uncategorized) + .toList(); + _sortedCollections = + CollectionSortUtil.getSortedCollections(regularCollections); + } else { + _sortedCollections = + CollectionSortUtil.getSortedCollections(collections); + } + }); + } + + @override + void initState() { + super.initState(); + _loadCollections(); + Bus.instance.on().listen((event) async { + await _loadCollections(); + }); + } + + Future _loadCollections() async { + setState(() { + _isLoading = true; + _error = null; + }); + + try { + final collections = await CollectionService.instance.getCollections(); + + final regularCollections = []; + Collection? uncategorized; + + for (final collection in collections) { + if (collection.type == CollectionType.uncategorized) { + uncategorized = collection; + } else { + regularCollections.add(collection); + } + } + + CollectionSortUtil.sortCollections(regularCollections); + + _allCollections = List.from(collections); + _sortedCollections = List.from(regularCollections); + _uncategorizedCollection = uncategorized; + _uncategorizedFileCount = uncategorized != null + ? (await CollectionService.instance + .getFilesInCollection(uncategorized)) + .length + : 0; + _allFiles = await CollectionService.instance.getAllFiles(); + + setState(() { + _isLoading = false; + }); + } catch (e) { + _logger.severe("Failed to load collections", e); + setState(() { + _error = 'Failed to load collections: $e'; + _isLoading = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: handleKeyEvent, + child: Scaffold( + appBar: AppBar( + leading: buildSearchLeading(), + title: Text(context.l10n.collections), + centerTitle: false, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Theme.of(context).textTheme.bodyLarge?.color, + actions: [ + buildSearchAction(), + ...buildSearchActions(), + ], + ), + body: _buildBody(), + ), + ); + } + + Widget _buildBody() { + if (_isLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + if (_error != null) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.error_outline, + color: Colors.red, + size: 64, + ), + const SizedBox(height: 16), + Text( + _error!, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: _loadCollections, + child: Text(context.l10n.retry), + ), + ], + ), + ); + } + + if (isSearchActive) { + return Padding( + padding: const EdgeInsets.only(top: 16), + child: SearchResultView( + collections: _sortedCollections, + files: const [], + searchQuery: searchQuery, + enableSorting: true, + isHomePage: false, + onSearchEverywhere: _searchEverywhere, + ), + ); + } + + if (_sortedCollections.isEmpty) { + if (searchQuery.isNotEmpty) { + return FileListViewHelpers.createSearchEmptyState( + searchQuery: searchQuery, + message: context.l10n.noCollectionsFoundForQuery(searchQuery), + ); + } else { + return Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.folder_open, + size: 64, + color: Colors.grey, + ), + const SizedBox(height: 16), + Text( + context.l10n.noCollectionsFound, + style: getEnteTextTheme(context).large.copyWith( + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } + } + + return Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + children: [ + if (searchQuery.isNotEmpty) + Container( + padding: const EdgeInsets.only(bottom: 16.0), + alignment: Alignment.centerLeft, + child: Text( + '${_sortedCollections.length} result${_sortedCollections.length == 1 ? '' : 's'} for "$searchQuery"', + style: getEnteTextTheme(context).small.copyWith( + color: Theme.of(context).textTheme.bodySmall?.color, + ), + ), + ), + Flexible( + child: ItemListView( + collections: _sortedCollections, + enableSorting: true, + ), + ), + if (!isSearchActive && _uncategorizedCollection != null) + _buildUncategorizedHook(), + _buildTrashHook(), + ], + ), + ); + } + + Widget _buildTrashHook() { + return Container( + margin: const EdgeInsets.only(top: 8.0, bottom: 16.0), + child: InkWell( + onTap: _openTrash, + borderRadius: BorderRadius.circular(12.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface.withOpacity(0.3), + border: Border.all( + color: Theme.of(context).dividerColor.withOpacity(0.5), + width: 0.5, + ), + borderRadius: BorderRadius.circular(12.0), + ), + child: Row( + children: [ + Icon( + Icons.delete_outline, + color: Theme.of(context) + .textTheme + .bodyLarge + ?.color + ?.withOpacity(0.7), + size: 22, + ), + const SizedBox(width: 12), + Expanded( + child: Text( + context.l10n.trash, + style: getEnteTextTheme(context).large.copyWith( + fontWeight: FontWeight.w500, + ), + ), + ), + Icon( + Icons.chevron_right, + color: Theme.of(context) + .textTheme + .bodyMedium + ?.color + ?.withOpacity(0.6), + size: 20, + ), + ], + ), + ), + ), + ); + } + + Future _openTrash() async { + final trashFiles = await TrashService.instance.getTrashFiles(); + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => TrashPage(trashFiles: trashFiles), + ), + ); + } + + void _searchEverywhere() { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => HomePage(initialSearchQuery: searchQuery), + ), + (route) => false, + ); + } + + Widget _buildUncategorizedHook() { + if (_uncategorizedCollection == null) return const SizedBox.shrink(); + + return Container( + margin: const EdgeInsets.only(top: 16.0, bottom: 8.0), + child: InkWell( + onTap: () => _openUncategorized(), + borderRadius: BorderRadius.circular(12.0), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface.withOpacity(0.3), + border: Border.all( + color: Theme.of(context).dividerColor.withOpacity(0.5), + width: 0.5, + ), + borderRadius: BorderRadius.circular(12.0), + ), + child: Row( + children: [ + Icon( + Icons.folder_open_outlined, + color: Theme.of(context) + .textTheme + .bodyLarge + ?.color + ?.withOpacity(0.7), + size: 22, + ), + const SizedBox(width: 12), + Expanded( + child: Row( + children: [ + Text( + context.l10n.uncategorized, + style: getEnteTextTheme(context).large.copyWith( + fontWeight: FontWeight.w500, + ), + ), + if (_uncategorizedFileCount! > 0) ...[ + const SizedBox(width: 8), + Text( + '•', + style: getEnteTextTheme(context).small.copyWith( + color: Theme.of(context) + .textTheme + .bodySmall + ?.color + ?.withOpacity(0.5), + ), + ), + const SizedBox(width: 8), + Text( + '${_uncategorizedFileCount!}', + style: getEnteTextTheme(context).small.copyWith( + color: Theme.of(context) + .textTheme + .bodySmall + ?.color + ?.withOpacity(0.7), + ), + ), + ], + ], + ), + ), + Icon( + Icons.chevron_right, + color: Theme.of(context) + .textTheme + .bodyMedium + ?.color + ?.withOpacity(0.6), + size: 20, + ), + ], + ), + ), + ), + ); + } + + Future _openUncategorized() async { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => + CollectionPage(collection: _uncategorizedCollection!), + ), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/pages/collection_page.dart b/mobile/apps/locker/lib/ui/pages/collection_page.dart new file mode 100644 index 0000000000..4185890b45 --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/collection_page.dart @@ -0,0 +1,275 @@ +import 'package:ente_events/event_bus.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/events/collections_updated_event.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/item_list_view.dart'; +import 'package:locker/ui/components/search_result_view.dart'; +import 'package:locker/ui/mixins/search_mixin.dart'; +import 'package:locker/ui/pages/home_page.dart'; +import 'package:locker/ui/pages/uploader_page.dart'; +import 'package:locker/utils/collection_actions.dart'; + +class CollectionPage extends UploaderPage { + final Collection collection; + + const CollectionPage({ + super.key, + required this.collection, + }); + + @override + State createState() => _CollectionPageState(); +} + +class _CollectionPageState extends UploaderPageState + with SearchMixin { + late Collection _collection; + List _files = []; + List _filteredFiles = []; + + @override + void onFileUploadComplete() { + CollectionService.instance.getCollections().then((collections) { + setState(() { + _initializeData(collections.where((c) => c.id == _collection.id).first); + }); + }); + } + + @override + List get allCollections => []; + + @override + List get allFiles => _files; + + @override + Collection get selectedCollection => _collection; + + @override + void onSearchResultsChanged( + List collections, List files,) { + setState(() { + _filteredFiles = files; + }); + } + + @override + void onSearchStateChanged(bool isActive) { + if (!isActive) { + setState(() { + _filteredFiles = _files; + }); + } + } + + List get _displayedFiles => + isSearchActive ? _filteredFiles : _files; + + @override + void initState() { + super.initState(); + _initializeData(widget.collection); + Bus.instance.on().listen((event) async { + final collection = (await CollectionService.instance.getCollections()) + .where( + (c) => c.id == widget.collection.id, + ) + .first; + await _initializeData(collection); + }); + } + + Future _initializeData(Collection collection) async { + _collection = collection; + _files = await CollectionService.instance.getFilesInCollection(_collection); + _filteredFiles = _files; + setState(() {}); + } + + Future _deleteCollection() async { + await CollectionActions.deleteCollection( + context, + _collection, + onSuccess: () { + if (mounted) { + Navigator.of(context).pop(); + } + }, + ); + } + + Future _editCollection() async { + await CollectionActions.editCollection( + context, + _collection, + onSuccess: () { + setState(() {}); + }, + ); + } + + @override + Widget build(BuildContext context) { + return KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: handleKeyEvent, + child: Scaffold( + appBar: _buildAppBar(), + body: _buildBody(), + floatingActionButton: + isSearchActive ? const SizedBox.shrink() : _buildFAB(), + ), + ); + } + + PreferredSizeWidget _buildAppBar() { + return AppBar( + leading: buildSearchLeading(), + title: Text( + _collection.name ?? context.l10n.untitled, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + elevation: 0, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Theme.of(context).textTheme.bodyLarge?.color, + actions: [ + buildSearchAction(), + ...buildSearchActions(), + _buildMenuButton(), + ], + ); + } + + Widget _buildMenuButton() { + return PopupMenuButton( + icon: const Icon(Icons.more_vert), + onSelected: (value) { + switch (value) { + case 'edit': + _editCollection(); + break; + case 'delete': + _deleteCollection(); + break; + } + }, + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + value: 'edit', + child: Row( + children: [ + const Icon(Icons.edit), + const SizedBox(width: 12), + Text(context.l10n.edit), + ], + ), + ), + PopupMenuItem( + value: 'delete', + child: Row( + children: [ + const Icon(Icons.delete, color: Colors.red), + const SizedBox(width: 12), + Text( + context.l10n.delete, + style: const TextStyle(color: Colors.red), + ), + ], + ), + ), + ]; + }, + ); + } + + Widget _buildBody() { + if (isSearchActive) { + return SearchResultView( + collections: const [], // CollectionPage primarily shows files + files: _filteredFiles, + searchQuery: searchQuery, + enableSorting: true, + isHomePage: false, + onSearchEverywhere: _searchEverywhere, + ); + } + + return SingleChildScrollView( + padding: const EdgeInsets.only(left: 16, right: 16), + child: _buildFilesList(), + ); + } + + Widget _buildFilesList() { + return _displayedFiles.isEmpty + ? SizedBox( + height: 400, + child: _buildEmptyState(), + ) + : ItemListView( + key: ValueKey(_displayedFiles.length), + files: _displayedFiles, + enableSorting: true, + ); + } + + Widget _buildEmptyState() { + return Padding( + padding: const EdgeInsets.all(32.0), + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + isSearchActive ? Icons.search_off : Icons.folder_off, + size: 64, + color: Colors.grey, + ), + const SizedBox(height: 16), + Text( + isSearchActive + ? 'No files found for "$searchQuery"' + : context.l10n.noFilesFound, + style: getEnteTextTheme(context).large.copyWith( + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + if (isSearchActive) ...[ + const SizedBox(height: 8), + Text( + 'Try adjusting your search query', + style: getEnteTextTheme(context).body.copyWith( + color: Colors.grey[600], + ), + textAlign: TextAlign.center, + ), + ], + ], + ), + ), + ); + } + + void _searchEverywhere() { + Navigator.of(context).pushAndRemoveUntil( + MaterialPageRoute( + builder: (context) => HomePage(initialSearchQuery: searchQuery), + ), + (route) => false, + ); + } + + Widget _buildFAB() { + return FloatingActionButton( + onPressed: addFile, + tooltip: context.l10n.addFiles, + child: const Icon(Icons.add), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/pages/home_page.dart b/mobile/apps/locker/lib/ui/pages/home_page.dart new file mode 100644 index 0000000000..86aa5b708e --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/home_page.dart @@ -0,0 +1,785 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:ente_events/event_bus.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:ente_utils/email_util.dart'; +import 'package:flutter/material.dart'; +import 'package:listen_sharing_intent/listen_sharing_intent.dart'; +import 'package:locker/events/collections_updated_event.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/ui/components/information_addition_dialog.dart'; +import 'package:locker/ui/components/recents_section_widget.dart'; +import 'package:locker/ui/components/search_result_view.dart'; +import 'package:locker/ui/mixins/search_mixin.dart'; +import 'package:locker/ui/pages/all_collections_page.dart'; +import 'package:locker/ui/pages/collection_page.dart'; +import 'package:locker/ui/pages/uploader_page.dart'; +import 'package:locker/utils/collection_actions.dart'; +import 'package:locker/utils/collection_sort_util.dart'; +import "package:locker/utils/snack_bar_utils.dart"; +import 'package:logging/logging.dart'; + +class HomePage extends UploaderPage { + final String? initialSearchQuery; + + const HomePage({super.key, this.initialSearchQuery}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends UploaderPageState + with TickerProviderStateMixin, SearchMixin { + List _collections = []; + List _filteredCollections = []; + List _recentFiles = []; + List _filteredFiles = []; + Map _collectionFileCounts = {}; + bool _isLoading = true; + String? _error; + final _logger = Logger('HomePage'); + StreamSubscription? _mediaStreamSubscription; + + @override + void onFileUploadComplete() { + _loadCollections(); + } + + @override + List get allCollections => _collections; + + @override + List get allFiles => _recentFiles; + + @override + void onSearchResultsChanged( + List collections, + List files, + ) { + setState(() { + _filteredCollections = + CollectionSortUtil.filterAndSortCollections(collections); + _filteredFiles = files; + }); + } + + @override + void onSearchStateChanged(bool isActive) { + if (!isActive) { + setState(() { + _filteredCollections = + CollectionSortUtil.filterAndSortCollections(_collections); + _filteredFiles = _recentFiles; + }); + } + } + + List get _displayedCollections { + final collections = isSearchActive ? _filteredCollections : _collections; + return CollectionSortUtil.filterAndSortCollections(collections); + } + + final ValueNotifier _isFabOpen = ValueNotifier(false); + late AnimationController _animationController; + late Animation _animation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + _animation = Tween( + begin: 0.0, + end: 1.0, + ).animate( + CurvedAnimation( + parent: _animationController, + curve: Curves.easeInOut, + ), + ); + + if (CollectionService.instance.hasCompletedFirstSync()) { + _loadCollections(); + } + + // Initialize sharing functionality to handle shared files + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + // Add a small delay to ensure the app is fully loaded + Future.delayed(const Duration(milliseconds: 1000), () { + if (mounted) { + initializeSharing(); + } + }); + } + }); + + // Activate search if initial query is provided (after collections are loaded) + if (widget.initialSearchQuery != null && + widget.initialSearchQuery!.isNotEmpty) { + WidgetsBinding.instance.addPostFrameCallback((_) { + // Wait a bit more to ensure collections are loaded + Future.delayed(const Duration(milliseconds: 100), () { + if (mounted) { + activateSearchWithQuery(widget.initialSearchQuery!); + } + }); + }); + } + + Bus.instance.on().listen((event) async { + await _loadCollections(); + }); + } + + @override + void dispose() { + _animationController.dispose(); + _isFabOpen.dispose(); + disposeSharing(); + super.dispose(); + } + + void initializeSharing() { + _logger.info('Initializing sharing functionality...'); + + try { + _mediaStreamSubscription = + ReceiveSharingIntent.instance.getMediaStream().listen( + (List value) { + _logger + .info('Received shared media files via stream: ${value.length}'); + for (var file in value) { + _logger.info('Shared file: ${file.path}, type: ${file.type}'); + } + if (value.isNotEmpty) { + _handleSharedFiles(value); + } + }, + onError: (err) { + _logger.severe('Error receiving shared media: $err'); + }, + ); + + _logger.info('Media stream subscription created successfully'); + } catch (e) { + _logger.severe('Error setting up media stream: $e'); + } + + _checkInitialSharedContent(); + } + + Future _checkInitialSharedContent() async { + try { + _logger.info('Checking for initial shared content...'); + + final initialMedia = + await ReceiveSharingIntent.instance.getInitialMedia(); + _logger.info('Initial media check result: ${initialMedia.length} files'); + + if (initialMedia.isNotEmpty) { + _logger + .info('Found initial shared media files: ${initialMedia.length}'); + for (var file in initialMedia) { + _logger.info('Initial shared file: ${file.path}, type: ${file.type}'); + } + await _handleSharedFiles(initialMedia); + } else { + _logger.info('No initial shared media files found'); + } + } catch (e) { + _logger.severe('Error checking initial shared content: $e'); + } + } + + Future _handleSharedFiles(List sharedFiles) async { + _logger.info('_handleSharedFiles called with ${sharedFiles.length} files'); + + if (!mounted) { + _logger.warning('Context not mounted, cannot handle shared files'); + return; + } + + try { + for (final sharedFile in sharedFiles) { + _logger.info('Processing shared file: ${sharedFile.path}'); + if (sharedFile.path.isNotEmpty) { + final file = File(sharedFile.path); + if (await file.exists()) { + _logger.info('File exists, uploading: ${sharedFile.path}'); + await uploadFile(file); + } else { + _logger.warning('Shared file does not exist: ${sharedFile.path}'); + } + } else { + _logger.warning('Shared file has empty path'); + } + } + + await ReceiveSharingIntent.instance.reset(); + _logger.info('Reset sharing intent after handling files'); + } catch (e) { + _logger.severe('Error handling shared files: $e'); + if (mounted) { + await showErrorDialog( + context, + 'Upload Error', + 'Failed to process shared files: $e', + ); + } + } + } + + void disposeSharing() { + _mediaStreamSubscription?.cancel(); + ReceiveSharingIntent.instance.reset(); + _logger.info('Sharing functionality disposed'); + } + + Future _loadCollections() async { + try { + setState(() { + _isLoading = true; + _error = null; + }); + + final collections = await CollectionService.instance.getCollections(); + await _loadRecentFiles(collections); + + final sortedCollections = + CollectionSortUtil.getSortedCollections(collections); + + setState(() { + _collections = sortedCollections; + _filteredCollections = + CollectionSortUtil.filterAndSortCollections(sortedCollections); + _filteredFiles = _recentFiles; + _isLoading = false; + }); + + await _loadCollectionFileCounts(); + } catch (error) { + setState(() { + _error = 'Error fetching collections: $error'; + _isLoading = false; + }); + } + } + + Future _loadRecentFiles(List collections) async { + final allFiles = await CollectionService.instance.getAllFiles(); + + final uniqueFilesMap = {}; + + for (final file in allFiles) { + final key = file.uploadedFileID?.toString() ?? file.toString(); + if (!uniqueFilesMap.containsKey(key)) { + uniqueFilesMap[key] = file; + } + } + + final uniqueFiles = uniqueFilesMap.values.toList(); + uniqueFiles.sort((a, b) { + final timeA = a.updationTime ?? a.modificationTime ?? 0; + final timeB = b.updationTime ?? b.modificationTime ?? 0; + return timeB.compareTo(timeA); + }); + + _recentFiles = uniqueFiles; + } + + void _navigateToCollection(Collection collection) { + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => CollectionPage(collection: collection), + ), + ); + } + + @override + Widget build(BuildContext context) { + return KeyboardListener( + focusNode: FocusNode(), + onKeyEvent: handleKeyEvent, + child: Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + leading: buildSearchLeading(), + title: GestureDetector( + onLongPress: () { + sendLogs( + context, + 'vishnu@ente.io', + subject: 'Locker logs', + body: 'Debug logs for Locker app.\n\n', + ); + }, + child: const Text( + 'Locker', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + elevation: 0, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Theme.of(context).textTheme.bodyLarge?.color, + actions: [ + buildSearchAction(), + ...buildSearchActions(), + ], + ), + body: _buildBody(), + floatingActionButton: + isSearchActive ? const SizedBox.shrink() : _buildMultiOptionFab(), + ), + ); + } + + Widget _buildBody() { + if (_isLoading) { + return const Center( + child: CircularProgressIndicator(), + ); + } + + if (_error != null) { + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: MediaQuery.of(context).size.height - 200, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.error_outline, + color: Colors.red, + size: 64, + ), + const SizedBox(height: 16), + Text( + _error!, + style: const TextStyle(color: Colors.red), + textAlign: TextAlign.center, + ), + const SizedBox(height: 16), + ElevatedButton( + onPressed: () => _loadCollections(), + child: Text(context.l10n.retry), + ), + ], + ), + ), + ), + ); + } + + if (isSearchActive) { + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: SearchResultView( + collections: _filteredCollections, + files: _filteredFiles, + searchQuery: searchQuery, + enableSorting: true, + isHomePage: true, + ), + ); + } + + if (_displayedCollections.isEmpty) { + return SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics(), + child: SizedBox( + height: MediaQuery.of(context).size.height - 200, + child: _buildEmptyState( + icon: Icons.folder_outlined, + title: context.l10n.noCollectionsFound, + subtitle: context.l10n.createYourFirstCollection, + action: Padding( + padding: const EdgeInsets.symmetric(horizontal: 40), + child: GradientButton( + onTap: _createCollection, + text: context.l10n.createCollection, + iconData: Icons.add, + paddingValue: 8.0, + ), + ), + ), + ), + ); + } + + return LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + physics: const AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 32, // Account for padding + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildCollectionsHeader(), + const SizedBox(height: 24), + _buildCollectionsGrid(), + const SizedBox(height: 24), + _buildRecentsSection(), + ], + ), + ), + ); + }, + ); + } + + Widget _buildRecentsSection() { + if (_recentFiles.isEmpty) { + return Container( + padding: const EdgeInsets.symmetric(vertical: 40), + child: _buildEmptyState( + icon: Icons.description_outlined, + title: context.l10n.nothingYet, + subtitle: context.l10n.uploadYourFirstDocument, + action: GradientButton( + onTap: addFile, + text: context.l10n.uploadDocument, + iconData: Icons.file_upload, + paddingValue: 8.0, + ), + ), + ); + } + return RecentsSectionWidget( + collections: CollectionSortUtil.filterAndSortCollections(_collections), + recentFiles: _recentFiles, + ); + } + + Future _createCollection() async { + final createdCollection = await CollectionActions.createCollection(context); + + if (createdCollection != null) { + await _loadCollections(); + _navigateToCollection(createdCollection); + } + } + + Future _addInformation() async { + final result = await showInformationAdditionDialog(context); + + if (result != null && mounted) { + switch (result.type) { + case InformationType.physicalDocument: + await _addPhysicalDocument(); + break; + case InformationType.emergencyContact: + await _addEmergencyContact(); + break; + case InformationType.accountCredential: + await _addAccountCredential(); + break; + } + } + } + + Future _addPhysicalDocument() async { + SnackBarUtils.showInfoSnackBar( + context, + "Soon", + ); + } + + Future _addEmergencyContact() async { + SnackBarUtils.showInfoSnackBar( + context, + "Soon", + ); + } + + Future _addAccountCredential() async { + SnackBarUtils.showInfoSnackBar( + context, + "Soon", + ); + } + + Widget _buildEmptyState({ + required IconData icon, + required String title, + required String subtitle, + Widget? action, + }) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(icon, size: 64, color: Colors.grey), + const SizedBox(height: 16), + Text( + title, + style: getEnteTextTheme(context).large.copyWith(color: Colors.grey), + ), + const SizedBox(height: 8), + Text( + subtitle, + style: getEnteTextTheme(context).body.copyWith(color: Colors.grey), + ), + if (action != null) ...[ + const SizedBox(height: 24), + action, + ], + ], + ), + ); + } + + Widget _buildFabLabel(String text) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + decoration: BoxDecoration( + color: getEnteColorScheme(context).fillBase, + borderRadius: BorderRadius.circular(16), + ), + child: Text( + text, + style: getEnteTextTheme(context).small.copyWith( + color: getEnteColorScheme(context).backgroundBase, + ), + ), + ); + } + + Widget _buildFabOption({ + required String label, + required IconData icon, + required VoidCallback onPressed, + required String heroTag, + }) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + _buildFabLabel(label), + const SizedBox(width: 8), + FloatingActionButton( + heroTag: heroTag, + mini: true, + onPressed: onPressed, + backgroundColor: getEnteColorScheme(context).fillBase, + child: Icon(icon), + ), + ], + ); + } + + Widget _buildCollectionsHeader() { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + SnackBarUtils.showWarningSnackBar(context, "Hello"); + Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => const AllCollectionsPage(), + ), + ); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + context.l10n.collections, + style: getEnteTextTheme(context).h3Bold, + ), + const Icon( + Icons.chevron_right, + color: Colors.grey, + ), + ], + ), + ); + } + + Widget _buildCollectionsGrid() { + return MediaQuery.removePadding( + context: context, + removeBottom: true, + removeTop: true, + child: GridView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 2.2, + ), + itemCount: min(_displayedCollections.length, 4), + itemBuilder: (context, index) { + final collection = _displayedCollections[index]; + final collectionName = collection.name ?? 'Unnamed Collection'; + + return GestureDetector( + onTap: () => _navigateToCollection(collection), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + color: getEnteColorScheme(context).fillFaint, + ), + padding: const EdgeInsets.all(12), + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + collectionName, + style: getEnteTextTheme(context).body.copyWith( + fontWeight: FontWeight.w500, + ), + textAlign: TextAlign.left, + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + const SizedBox(height: 4), + Text( + context.l10n + .items(_collectionFileCounts[collection.id] ?? 0), + style: getEnteTextTheme(context).small.copyWith( + color: Colors.grey[600], + ), + textAlign: TextAlign.left, + ), + ], + ), + if (collection.type == CollectionType.favorites) + Positioned( + top: 0, + right: 0, + child: Icon( + Icons.star, + color: getEnteColorScheme(context).primary500, + size: 18, + ), + ), + ], + ), + ), + ); + }, + ), + ); + } + + Widget _buildMultiOptionFab() { + return ValueListenableBuilder( + valueListenable: _isFabOpen, + builder: (context, isFabOpen, child) { + return Stack( + children: [ + if (isFabOpen) + Positioned.fill( + child: GestureDetector( + onTap: _toggleFab, + child: Container( + color: Colors.transparent, + ), + ), + ), + Positioned( + right: 0, + bottom: 0, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + if (isFabOpen) ...[ + ScaleTransition( + scale: _animation, + child: Container( + margin: const EdgeInsets.only(bottom: 16), + child: _buildFabOption( + label: context.l10n.addInformation, + icon: Icons.post_add, + onPressed: () { + _toggleFab(); + _addInformation(); + }, + heroTag: "addInformation", + ), + ), + ), + if (_collections.isNotEmpty) + ScaleTransition( + scale: _animation, + child: Container( + margin: const EdgeInsets.only(bottom: 8), + child: _buildFabOption( + label: context.l10n.uploadDocumentTooltip, + icon: Icons.file_upload, + onPressed: () { + _toggleFab(); + addFile(); + }, + heroTag: "addFile", + ), + ), + ), + ], + FloatingActionButton( + onPressed: _toggleFab, + child: AnimatedRotation( + turns: isFabOpen ? 0.125 : 0.0, // 45 degrees when open + duration: const Duration(milliseconds: 300), + child: const Icon(Icons.add), + ), + ), + ], + ), + ), + ], + ); + }, + ); + } + + void _toggleFab() { + _isFabOpen.value = !_isFabOpen.value; + + if (_isFabOpen.value) { + _animationController.forward(); + } else { + _animationController.reverse(); + } + } + + Future _loadCollectionFileCounts() async { + final counts = {}; + + for (final collection in _displayedCollections.take(4)) { + try { + final files = + await CollectionService.instance.getFilesInCollection(collection); + counts[collection.id] = files.length; + } catch (e) { + counts[collection.id] = 0; + } + } + + if (mounted) { + setState(() { + _collectionFileCounts = counts; + }); + } + } +} diff --git a/mobile/apps/locker/lib/ui/pages/onboarding_page.dart b/mobile/apps/locker/lib/ui/pages/onboarding_page.dart new file mode 100644 index 0000000000..a5654f0e3a --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/onboarding_page.dart @@ -0,0 +1,227 @@ +import 'package:ente_accounts/pages/email_entry_page.dart'; +import 'package:ente_accounts/pages/login_page.dart'; +import 'package:ente_accounts/pages/password_entry_page.dart'; +import 'package:ente_accounts/pages/password_reentry_page.dart'; +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/gradient_button.dart'; +import 'package:ente_ui/components/developer_settings_widget.dart'; +import "package:ente_ui/pages/developer_settings_page.dart"; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/configuration.dart'; +import 'package:locker/ui/pages/home_page.dart'; + +class OnboardingPage extends StatefulWidget { + const OnboardingPage({super.key}); + + @override + State createState() => _OnboardingPageState(); +} + +class _OnboardingPageState extends State { + static const kDeveloperModeTapCountThreshold = 7; + + int _developerModeTapCount = 0; + + @override + Widget build(BuildContext context) { + debugPrint("Building OnboardingPage"); + final l10n = context.l10n; + return Scaffold( + body: SafeArea( + child: GestureDetector( + onTap: () async { + _developerModeTapCount++; + if (_developerModeTapCount >= kDeveloperModeTapCountThreshold) { + _developerModeTapCount = 0; + final result = await showChoiceDialog( + context, + title: l10n.developerSettings, + firstButtonLabel: l10n.yes, + body: l10n.developerSettingsWarning, + isDismissible: false, + ); + if (result?.action == ButtonAction.first) { + await Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return DeveloperSettingsPage(Configuration.instance); + }, + ), + ); + setState(() {}); + } + } + }, + child: LayoutBuilder( + builder: (context, constraints) { + return SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight, + maxWidth: 450, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 40.0, + horizontal: 40, + ), + child: Column( + children: [ + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image.asset( + "assets/locker.png", + width: 200, + height: 200, + ), + const SizedBox(height: 12), + Text( + "ente", + style: getEnteTextTheme(context).h1.copyWith( + fontWeight: FontWeight.bold, + fontFamily: 'Montserrat', + ), + ), + const SizedBox(height: 4), + Text( + "Locker", + style: Theme.of(context) + .textTheme + .headlineMedium, + ), + const SizedBox(height: 32), + Text( + l10n.onBoardingBody, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .titleLarge! + .copyWith( + color: Colors.white38, + ), + ), + ], + ), + ), + Container( + width: double.infinity, + padding: const EdgeInsets.symmetric(horizontal: 20), + child: GradientButton( + onTap: _navigateToSignUpPage, + text: l10n.newUser, + ), + ), + const SizedBox(height: 16), + Container( + height: 56, + width: double.infinity, + padding: const EdgeInsets.fromLTRB(20, 0, 20, 0), + child: Hero( + tag: "log_in", + child: ElevatedButton( + style: ElevatedButton.styleFrom().copyWith( + shape: WidgetStateProperty.all< + RoundedRectangleBorder>( + RoundedRectangleBorder( + borderRadius: BorderRadius.circular(32.0), + ), + ), + ), + onPressed: _navigateToSignInPage, + child: Text( + l10n.existingUser, + style: const TextStyle( + color: Colors.white, // same for both themes + ), + ), + ), + ), + ), + const SizedBox(height: 4), + DeveloperSettingsWidget(Configuration.instance), + ], + ), + ), + ), + ), + ); + }, + ), + ), + ), + ); + } + + void _navigateToSignUpPage() { + Widget page; + if (Configuration.instance.getEncryptedToken() == null) { + page = EmailEntryPage(Configuration.instance); + } else { + // No key + if (Configuration.instance.getKeyAttributes() == null) { + // Never had a key + page = PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.set, + const HomePage(), + ); + } else if (Configuration.instance.getKey() == null) { + // Yet to decrypt the key + page = PasswordReentryPage( + Configuration.instance, + const HomePage(), + ); + } else { + // All is well, user just has not subscribed + page = const HomePage(); + } + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return page; + }, + ), + ); + } + + void _navigateToSignInPage() { + Widget page; + if (Configuration.instance.getEncryptedToken() == null) { + page = LoginPage(Configuration.instance); + } else { + // No key + if (Configuration.instance.getKeyAttributes() == null) { + // Never had a key + page = PasswordEntryPage( + Configuration.instance, + PasswordEntryMode.set, + const HomePage(), + ); + } else if (Configuration.instance.getKey() == null) { + // Yet to decrypt the key + page = PasswordReentryPage( + Configuration.instance, + const HomePage(), + ); + } else { + // All is well, user just has not subscribed + // page = getSubscriptionPage(isOnBoarding: true); + page = const HomePage(); + } + } + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) { + return page; + }, + ), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/pages/trash_page.dart b/mobile/apps/locker/lib/ui/pages/trash_page.dart new file mode 100644 index 0000000000..5f4261898d --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/trash_page.dart @@ -0,0 +1,404 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/models/file.dart'; +import 'package:locker/services/trash/models/trash_file.dart'; +import 'package:locker/services/trash/trash_service.dart'; +import 'package:locker/ui/components/item_list_view.dart'; +import 'package:locker/utils/snack_bar_utils.dart'; + +class TrashPage extends StatefulWidget { + final List trashFiles; + + const TrashPage({ + super.key, + required this.trashFiles, + }); + + @override + State createState() => _TrashPageState(); +} + +class _TrashPageState extends State { + List _sortedTrashFiles = []; + List _allTrashFiles = []; + + @override + void initState() { + super.initState(); + _allTrashFiles = List.from(widget.trashFiles); + _sortedTrashFiles = List.from(widget.trashFiles); + } + + List _getFileOverflowActions() { + return [ + OverflowMenuAction( + id: 'restore', + label: context.l10n.restore, + icon: Icons.restore, + onTap: (context, file, collection) { + _restoreFile(context, file!); + }, + ), + OverflowMenuAction( + id: 'delete', + label: context.l10n.delete, + icon: Icons.delete_forever, + onTap: (context, file, collection) { + _deleteFilePermanently(context, file!); + }, + ), + ]; + } + + void _restoreFile(BuildContext context, EnteFile file) async { + final collections = await CollectionService.instance.getCollections(); + + final availableCollections = collections + .where((c) => !c.isDeleted && c.type != CollectionType.uncategorized) + .toList(); + + if (availableCollections.isEmpty) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.noCollectionsAvailableForRestore, + ); + return; + } + + final selectedCollection = await _showCollectionPickerDialog( + context, + availableCollections, + file.displayName, + ); + + if (selectedCollection != null) { + await _performRestore(context, file, selectedCollection); + } + } + + void _deleteFilePermanently(BuildContext context, EnteFile file) { + TrashService.instance.deleteFromTrash([file]).then((_) { + setState(() { + _sortedTrashFiles.remove(file); + _allTrashFiles.remove(file); + }); + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.deletedPermanently(file.displayName), + ); + }).catchError((error) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToDeleteFile(error.toString()), + ); + }); + } + + Future _showCollectionPickerDialog( + BuildContext context, + List collections, + String fileName, + ) async { + return showDialog( + context: context, + barrierColor: getEnteColorScheme(context).backdropBase, + builder: (context) => _CollectionPickerDialog( + collections: collections, + fileName: fileName, + ), + ); + } + + Future _performRestore( + BuildContext context, + EnteFile file, + Collection targetCollection, + ) async { + final dialog = createProgressDialog( + context, + context.l10n.restoring, + isDismissible: false, + ); + + try { + await dialog.show(); + + await TrashService.instance.restore([file], targetCollection); + + setState(() { + _sortedTrashFiles.remove(file); + _allTrashFiles.remove(file); + }); + + await dialog.hide(); + + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.restoredFileToCollection( + file.displayName, + targetCollection.name ?? 'Unnamed Collection', + ), + ); + } catch (error) { + await dialog.hide(); + + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToRestoreFile(file.displayName, error.toString()), + ); + } + } + + Future _emptyTrash() async { + final result = await showChoiceDialog( + context, + title: context.l10n.emptyTrash, + body: context.l10n.emptyTrashConfirmation, + firstButtonLabel: context.l10n.emptyTrash, + secondButtonLabel: context.l10n.cancel, + firstButtonType: ButtonType.critical, + isCritical: true, + ); + + if (result?.action == ButtonAction.first && context.mounted) { + await _performEmptyTrash(); + } + } + + Future _performEmptyTrash() async { + final dialog = createProgressDialog( + context, + context.l10n.clearingTrash, + isDismissible: false, + ); + await dialog.show(); + try { + await TrashService.instance.emptyTrash(); + setState(() { + _sortedTrashFiles.clear(); + _allTrashFiles.clear(); + }); + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.trashClearedSuccessfully, + ); + Navigator.of(context).pop(); + } catch (error) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToClearTrash(error.toString()), + ); + } finally { + await dialog.hide(); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(context.l10n.trash), + centerTitle: false, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + foregroundColor: Theme.of(context).textTheme.bodyLarge?.color, + actions: [ + IconButton( + icon: const Icon(Icons.delete_sweep), + onPressed: _emptyTrash, + tooltip: context.l10n.emptyTrashTooltip, + ), + ], + ), + body: _buildBody(), + ); + } + + Widget _buildBody() { + if (_sortedTrashFiles.isEmpty) { + return Center( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.delete_outline, + size: 64, + color: Colors.grey, + ), + const SizedBox(height: 16), + Text( + context.l10n.trashIsEmpty, + style: getEnteTextTheme(context).large.copyWith( + color: Colors.grey, + ), + textAlign: TextAlign.center, + ), + ], + ), + ), + ); + } + + return Padding( + padding: const EdgeInsets.all(16.0), + child: ItemListView( + files: _sortedTrashFiles.cast(), + enableSorting: true, + fileOverflowActions: _getFileOverflowActions(), + ), + ); + } +} + +class _CollectionPickerDialog extends StatefulWidget { + final List collections; + final String fileName; + + const _CollectionPickerDialog({ + required this.collections, + required this.fileName, + }); + + @override + State<_CollectionPickerDialog> createState() => + _CollectionPickerDialogState(); +} + +class _CollectionPickerDialogState extends State<_CollectionPickerDialog> { + @override + Widget build(BuildContext context) { + final colorScheme = getEnteColorScheme(context); + final textTheme = getEnteTextTheme(context); + + return Dialog( + backgroundColor: colorScheme.backgroundElevated, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8), + ), + child: Container( + width: 400, + constraints: const BoxConstraints(maxHeight: 600), + padding: const EdgeInsets.all(20), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + const Icon( + Icons.restore, + color: Colors.green, + size: 24, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + context.l10n.restoreFile(widget.fileName), + style: textTheme.largeBold, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 20), + Flexible( + child: Container( + height: 150, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8), + border: Border.all(color: colorScheme.strokeFaint), + ), + child: widget.collections.isEmpty + ? Container( + padding: const EdgeInsets.all(16), + child: Center( + child: Text( + context.l10n.noCollectionsAvailable, + style: textTheme.body.copyWith( + color: colorScheme.textMuted, + ), + ), + ), + ) + : Scrollbar( + thumbVisibility: true, + thickness: 6, + radius: const Radius.circular(3), + child: GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + childAspectRatio: 3.5, + crossAxisSpacing: 6, + mainAxisSpacing: 6, + ), + padding: const EdgeInsets.all(6), + itemCount: widget.collections.length, + itemBuilder: (context, index) { + final collection = widget.collections[index]; + final collectionName = + collection.name ?? 'Unnamed Collection'; + + return InkWell( + onTap: () => + Navigator.of(context).pop(collection), + borderRadius: BorderRadius.circular(8), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 6, + vertical: 4, + ), + decoration: BoxDecoration( + color: colorScheme.fillFaint, + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: colorScheme.strokeFaint, + width: 1, + ), + ), + child: Center( + child: Text( + collectionName, + style: textTheme.small.copyWith( + color: colorScheme.textBase, + fontWeight: FontWeight.normal, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ), + ), + ); + }, + ), + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: ButtonWidget( + buttonType: ButtonType.secondary, + labelText: context.l10n.cancel, + onTap: () async => Navigator.of(context).pop(), + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/mobile/apps/locker/lib/ui/pages/uploader_page.dart b/mobile/apps/locker/lib/ui/pages/uploader_page.dart new file mode 100644 index 0000000000..ae2e3145fc --- /dev/null +++ b/mobile/apps/locker/lib/ui/pages/uploader_page.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:ente_ui/pages/base_home_page.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/services/files/sync/metadata_updater_service.dart'; +import 'package:locker/services/files/upload/file_upload_service.dart'; +import 'package:locker/ui/components/file_upload_dialog.dart'; +import 'package:logging/logging.dart'; + +/// Abstract base class that provides file upload functionality. +/// Contains common file picking and uploading logic that can be reused +/// across different pages like HomePage and CollectionPage. +abstract class UploaderPage extends BaseHomePage { + const UploaderPage({super.key}); +} + +abstract class UploaderPageState extends State { + final _logger = Logger('UploaderPage'); + + /// Returns the collection that should be pre-selected in the upload dialog. + /// Return null to default to uncategorized collection. + Collection? get selectedCollection => null; + + /// Called after a successful file upload to refresh the UI + void onFileUploadComplete(); + + /// Opens a file picker dialog and uploads the selected file + Future addFile() async { + final FilePickerResult? result = + await FilePicker.platform.pickFiles(type: FileType.any); + + if (result != null && result.files.isNotEmpty) { + final selectedFile = result.files.first; + if (selectedFile.path != null) { + await uploadFile(File(selectedFile.path!)); + } + } + } + + Future uploadFile(File file) async { + final progressDialog = createProgressDialog(context, "Uploading..."); + try { + final List futures = []; + + final collections = await CollectionService.instance.getCollections(); + + final collectionsWithoutUncategorized = collections + .where((c) => c.type != CollectionType.uncategorized) + .toList(); + + final uploadResult = await showFileUploadDialog( + context, + file: file, + collections: collectionsWithoutUncategorized, + selectedCollection: selectedCollection, + ); + + if (uploadResult != null && uploadResult.selectedCollections.isNotEmpty) { + final fileUploadFuture = FileUploader.instance + .upload(file, uploadResult.selectedCollections.first); + futures.add(fileUploadFuture); + futures.add( + fileUploadFuture.then((enteFile) async { + // Add to additional collections if multiple were selected + for (int cIndex = 1; + cIndex < uploadResult.selectedCollections.length; + cIndex++) { + futures.add( + CollectionService.instance.addToCollection( + uploadResult.selectedCollections[cIndex], + enteFile, + ), + ); + } + + if (uploadResult.note.isNotEmpty) { + futures.add( + MetadataUpdaterService.instance + .editFileCaption(enteFile, uploadResult.note), + ); + } + }).catchError((e) { + _logger.severe('File upload failed', e); + }), + ); + } + + if (futures.isNotEmpty) { + await progressDialog.show(); + await Future.wait(futures); + _logger.info('File upload completed successfully'); + await CollectionService.instance.sync(); + onFileUploadComplete(); + } + } catch (e, s) { + _logger.severe('Failed to upload file', e, s); + await showGenericErrorDialog( + context: context, + error: e, + ); + } finally { + if (progressDialog.isShowing()) { + await progressDialog.hide(); + } + } + } +} diff --git a/mobile/apps/locker/lib/utils/collection_actions.dart b/mobile/apps/locker/lib/utils/collection_actions.dart new file mode 100644 index 0000000000..90bdc76473 --- /dev/null +++ b/mobile/apps/locker/lib/utils/collection_actions.dart @@ -0,0 +1,160 @@ +import 'package:ente_ui/components/buttons/button_widget.dart'; +import 'package:ente_ui/components/buttons/models/button_type.dart'; +import 'package:ente_ui/utils/dialog_util.dart'; +import 'package:flutter/material.dart'; +import 'package:locker/l10n/l10n.dart'; +import 'package:locker/services/collections/collections_service.dart'; +import 'package:locker/services/collections/models/collection.dart'; +import 'package:locker/utils/snack_bar_utils.dart'; +import 'package:logging/logging.dart'; + +/// Utility class for common collection actions like edit and delete +class CollectionActions { + static final _logger = Logger('CollectionActions'); + + /// Shows a dialog to create a new collection + static Future createCollection( + BuildContext context, { + bool autoSelectInParent = false, + }) async { + Collection? createdCollection; + + final nameSuggestion = + await CollectionService.instance.getRandomUnusedCollectionName(); + + final result = await showTextInputDialog( + context, + title: context.l10n.createNewCollection, + submitButtonLabel: context.l10n.create, + hintText: nameSuggestion, + alwaysShowSuccessState: true, + textCapitalization: TextCapitalization.words, + onSubmit: (String text) async { + // indicates user cancelled the creation request + if (text.trim().isEmpty) { + return; + } + + try { + createdCollection = + await CollectionService.instance.createCollection(text.trim()); + } catch (e, s) { + _logger.severe('Failed to create collection', e, s); + rethrow; + } + }, + ); + + if (result is Exception) { + if (context.mounted) { + await showGenericErrorDialog( + context: context, + error: result, + ); + } + return null; + } else if (createdCollection != null) { + return createdCollection; + } + + return null; + } + + // Shows a dialog to edit/rename a collection + static Future editCollection( + BuildContext context, + Collection collection, { + VoidCallback? onSuccess, + }) async { + await showTextInputDialog( + context, + title: context.l10n.renameCollection, + initialValue: collection.name ?? '', + hintText: context.l10n.documentsHint, + submitButtonLabel: context.l10n.save, + onSubmit: (String newName) async { + if (newName.isEmpty || newName == collection.name) return; + + final progressDialog = + createProgressDialog(context, context.l10n.pleaseWait); + await progressDialog.show(); + + try { + await CollectionService.instance.rename(collection, newName); + await progressDialog.hide(); + + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.collectionRenamedSuccessfully, + ); + + // Update the collection name locally + collection.setName(newName); + + // Call success callback if provided + onSuccess?.call(); + } catch (error) { + await progressDialog.hide(); + + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToRenameCollection(error.toString()), + ); + } + }, + ); + } + + /// Shows a confirmation dialog and deletes a collection + static Future deleteCollection( + BuildContext context, + Collection collection, { + VoidCallback? onSuccess, + }) async { + if (!collection.type.canDelete) { + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.collectionCannotBeDeleted, + ); + return; + } + + final collectionName = collection.name ?? 'this collection'; + + final dialogChoice = await showChoiceDialog( + context, + title: context.l10n.deleteCollection, + body: context.l10n.deleteCollectionConfirmation(collectionName), + firstButtonLabel: context.l10n.delete, + secondButtonLabel: context.l10n.cancel, + firstButtonType: ButtonType.critical, + isCritical: true, + ); + + if (dialogChoice?.action != ButtonAction.first) return; + + final progressDialog = + createProgressDialog(context, context.l10n.pleaseWait); + await progressDialog.show(); + + try { + await CollectionService.instance.trashCollection(collection); + await progressDialog.hide(); + + SnackBarUtils.showInfoSnackBar( + context, + context.l10n.collectionDeletedSuccessfully, + ); + + // Call success callback if provided + onSuccess?.call(); + } catch (error) { + await progressDialog.hide(); + + SnackBarUtils.showWarningSnackBar( + context, + context.l10n.failedToDeleteCollection(error.toString()), + ); + } + } +} diff --git a/mobile/apps/locker/lib/utils/collection_sort_util.dart b/mobile/apps/locker/lib/utils/collection_sort_util.dart new file mode 100644 index 0000000000..80e12f9426 --- /dev/null +++ b/mobile/apps/locker/lib/utils/collection_sort_util.dart @@ -0,0 +1,101 @@ +import 'package:locker/services/collections/models/collection.dart'; + +/// Utility class for sorting collections with consistent logic across the app +class CollectionSortUtil { + /// Sorts collections with Important (favorites) collection first, then alphabetically by name + static void sortCollections(List collections) { + collections.sort((a, b) { + // Important collection (favorites) should come first + if (a.type == CollectionType.favorites && + b.type != CollectionType.favorites) { + return -1; + } + if (b.type == CollectionType.favorites && + a.type != CollectionType.favorites) { + return 1; + } + // For other collections, sort alphabetically by name + final nameA = a.name ?? a.name ?? ''; + final nameB = b.name ?? b.name ?? ''; + return nameA.toLowerCase().compareTo(nameB.toLowerCase()); + }); + } + + /// Returns a new sorted list of collections with Important (favorites) first + static List getSortedCollections(List collections) { + final sortedList = List.from(collections); + sortCollections(sortedList); + return sortedList; + } + + /// Filters out uncategorized collections and sorts the remaining ones + static List filterAndSortCollections( + List collections, + ) { + final filtered = collections + .where((c) => c.type != CollectionType.uncategorized) + .toList(); + sortCollections(filtered); + return filtered; + } + + /// Compares two collections for sorting, prioritizing Important collection + /// Returns -1 if a should come before b, 1 if b should come before a, 0 if equal + static int compareCollections(Collection a, Collection b) { + // Important collection (favorites) should come first + if (a.type == CollectionType.favorites && + b.type != CollectionType.favorites) { + return -1; + } + if (b.type == CollectionType.favorites && + a.type != CollectionType.favorites) { + return 1; + } + // For other collections, sort alphabetically by name + final nameA = a.name ?? a.name ?? ''; + final nameB = b.name ?? b.name ?? ''; + return nameA.toLowerCase().compareTo(nameB.toLowerCase()); + } + + /// Compares two collections for sorting with Important always first regardless of sort direction + /// Used for table sorting where Important should always be first + static int compareCollectionsWithFavoritesPriority( + Collection a, + Collection b, + bool ascending, + ) { + // Important collection (favorites) should always come first regardless of sort direction + if (a.type == CollectionType.favorites && + b.type != CollectionType.favorites) { + return -1; + } + if (b.type == CollectionType.favorites && + a.type != CollectionType.favorites) { + return 1; + } + // For other collections, use normal comparison + return ascending ? compareCollections(a, b) : compareCollections(b, a); + } + + /// Compares two collections for date sorting with Important always first regardless of sort direction + /// Used for table sorting where Important should always be first + static int compareCollectionsByDateWithFavoritesPriority( + Collection a, + Collection b, + bool ascending, + ) { + // Important collection (favorites) should always come first regardless of sort direction + if (a.type == CollectionType.favorites && + b.type != CollectionType.favorites) { + return -1; + } + if (b.type == CollectionType.favorites && + a.type != CollectionType.favorites) { + return 1; + } + // For other collections, sort by modification time + final dateA = DateTime.fromMicrosecondsSinceEpoch(a.updationTime); + final dateB = DateTime.fromMicrosecondsSinceEpoch(b.updationTime); + return ascending ? dateA.compareTo(dateB) : dateB.compareTo(dateA); + } +} diff --git a/mobile/apps/locker/lib/utils/crypto_helper.dart b/mobile/apps/locker/lib/utils/crypto_helper.dart new file mode 100644 index 0000000000..8ad4329af7 --- /dev/null +++ b/mobile/apps/locker/lib/utils/crypto_helper.dart @@ -0,0 +1,48 @@ +import "dart:typed_data"; + +import "package:ente_crypto_dart/ente_crypto_dart.dart"; +import "package:locker/services/collections/models/collection.dart"; +import "package:locker/services/configuration.dart"; + +class CryptoHelper { + CryptoHelper._privateConstructor(); + static final CryptoHelper instance = CryptoHelper._privateConstructor(); + + Uint8List getFileKey( + String encryptedKey, + String keyDecryptionNonce, + Uint8List collectionKey, + ) { + final eKey = CryptoUtil.base642bin(encryptedKey); + final nonce = CryptoUtil.base642bin(keyDecryptionNonce); + return CryptoUtil.decryptSync(eKey, collectionKey, nonce); + } + + Uint8List getCollectionKey(Collection collection) { + final encryptedKey = CryptoUtil.base642bin(collection.encryptedKey); + Uint8List? collectionKey; + if (collection.owner.id == Configuration.instance.getUserID()) { + // If the collection is owned by the user, decrypt with the master key + if (Configuration.instance.getKey() == null) { + // Possible during AppStore account migration, where SecureStorage + // would become inaccessible to the new Developer Account + throw Exception("key can not be null"); + } + collectionKey = CryptoUtil.decryptSync( + encryptedKey, + Configuration.instance.getKey()!, + CryptoUtil.base642bin(collection.keyDecryptionNonce!), + ); + } else { + // If owned by a different user, decrypt with the public key + collectionKey = CryptoUtil.openSealSync( + encryptedKey, + CryptoUtil.base642bin( + Configuration.instance.getKeyAttributes()!.publicKey, + ), + Configuration.instance.getSecretKey()!, + ); + } + return collectionKey; + } +} diff --git a/mobile/apps/locker/lib/utils/data_util.dart b/mobile/apps/locker/lib/utils/data_util.dart new file mode 100644 index 0000000000..d324cde35d --- /dev/null +++ b/mobile/apps/locker/lib/utils/data_util.dart @@ -0,0 +1,63 @@ +import 'dart:math'; + +final storageUnits = ["bytes", "KB", "MB", "GB", "TB"]; + +String convertBytesToReadableFormat(int bytes) { + int storageUnitIndex = 0; + while (bytes >= 1024 && storageUnitIndex < storageUnits.length - 1) { + storageUnitIndex++; + bytes = (bytes / 1024).round(); + } + return "$bytes ${storageUnits[storageUnitIndex]}"; +} + +(int, String) convertBytesToNumberAndUnit(int bytes) { + int storageUnitIndex = 0; + while (bytes >= 1024 && storageUnitIndex < storageUnits.length - 1) { + storageUnitIndex++; + bytes = (bytes / 1024).round(); + } + return (bytes, storageUnits[storageUnitIndex]); +} + +String formatBytes(int bytes, [int decimals = 2]) { + if (bytes == 0) return '0 bytes'; + const k = 1024; + final int dm = decimals < 0 ? 0 : decimals; + final int i = (log(bytes) / log(k)).floor(); + return '${(bytes / pow(k, i)).toStringAsFixed(dm)} ${storageUnits[i]}'; +} + +//shows 1st decimal only if less than 10GB & omits decimal if decimal is 0 +num roundBytesUsedToGBs(int usedBytes, int freeSpace) { + const tenGBinBytes = 10737418240; + num bytesInGB = convertBytesToGBs(usedBytes); + if ((usedBytes >= tenGBinBytes && freeSpace >= tenGBinBytes) || + bytesInGB % 1 == 0) { + bytesInGB = bytesInGB.truncate(); + } + return bytesInGB; +} + +//Eg: 0.3 GB, 11.0 GB, 532.3 GB +num convertBytesToGBs(int bytes) { + return num.parse((bytes / (pow(1024, 3))).toStringAsFixed(1)); +} + +int convertBytesToAbsoluteGBs(int bytes) { + return (bytes / pow(1024, 3)).round(); +} + +int convertBytesToMBs(int bytes) { + return (bytes / pow(1024, 2)).round(); +} + +//Eg: 1TB, 1.3TB, 4.9TB, 3TB +num roundGBsToTBs(sizeInGBs) { + final num sizeInTBs = num.parse((sizeInGBs / 1000).toStringAsFixed(1)); + if (sizeInTBs % 1 == 0) { + return sizeInTBs.truncate(); + } else { + return sizeInTBs; + } +} diff --git a/mobile/apps/locker/lib/utils/date_time_util.dart b/mobile/apps/locker/lib/utils/date_time_util.dart new file mode 100644 index 0000000000..3e3ea6850d --- /dev/null +++ b/mobile/apps/locker/lib/utils/date_time_util.dart @@ -0,0 +1,259 @@ +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:locker/l10n/l10n.dart'; + +const Set monthWith31Days = {1, 3, 5, 7, 8, 10, 12}; +const Set monthWith30Days = {4, 6, 9, 11}; +Map _months = { + 1: "Jan", + 2: "Feb", + 3: "March", + 4: "April", + 5: "May", + 6: "Jun", + 7: "July", + 8: "Aug", + 9: "Sep", + 10: "Oct", + 11: "Nov", + 12: "Dec", +}; + +Map _fullMonths = { + 1: "January", + 2: "February", + 3: "March", + 4: "April", + 5: "May", + 6: "June", + 7: "July", + 8: "August", + 9: "September", + 10: "October", + 11: "November", + 12: "December", +}; + +Map _days = { + 1: "Mon", + 2: "Tue", + 3: "Wed", + 4: "Thu", + 5: "Fri", + 6: "Sat", + 7: "Sun", +}; + +final currentYear = int.parse(DateTime.now().year.toString()); +const searchStartYear = 1970; + +//Jun 2022 +String getMonthAndYear(DateTime dateTime) { + return "${_months[dateTime.month]!} ${dateTime.year}"; +} + +//Thu, 30 Jun +String getDayAndMonth(DateTime dateTime) { + return "${_days[dateTime.weekday]!}, ${dateTime.day} ${_months[dateTime.month]!}"; +} + +//30 Jun, 2022 +String getDateAndMonthAndYear(DateTime dateTime) { + return "${dateTime.day} ${_months[dateTime.month]!}, ${dateTime.year}"; +} + +String getDay(DateTime dateTime) { + return _days[dateTime.weekday]!; +} + +String getMonth(DateTime dateTime) { + return _months[dateTime.month]!; +} + +String getFullMonth(DateTime dateTime) { + return _fullMonths[dateTime.month]!; +} + +String getAbbreviationOfYear(DateTime dateTime) { + return (dateTime.year % 100).toString(); +} + +//14:32 +String getTime(DateTime dateTime) { + final hours = + dateTime.hour > 9 ? dateTime.hour.toString() : "0${dateTime.hour}"; + final minutes = + dateTime.minute > 9 ? dateTime.minute.toString() : "0${dateTime.minute}"; + return "$hours:$minutes"; +} + +//11:22 AM +String getTimeIn12hrFormat(DateTime dateTime) { + return DateFormat.jm().format(dateTime); +} + +//Thu, Jun 30, 2022 - 14:32 +String getFormattedTime(DateTime dateTime) { + return "${getDay(dateTime)}, ${getMonth(dateTime)} ${dateTime.day}, ${dateTime.year} - ${getTime(dateTime)}"; +} + +//30 Jun'22 +String getFormattedDate(DateTime dateTime) { + return "${dateTime.day} ${getMonth(dateTime)}'${getAbbreviationOfYear(dateTime)}"; +} + +String getFullDate(DateTime dateTime) { + return "${getDay(dateTime)}, ${getMonth(dateTime)} ${dateTime.day} ${dateTime.year}"; +} + +String daysLeft(int futureTime) { + final int daysLeft = ((futureTime - DateTime.now().microsecondsSinceEpoch) / + Duration.microsecondsPerDay) + .ceil(); + return '$daysLeft day${daysLeft <= 1 ? "" : "s"}'; +} + +String formatDuration(Duration position) { + final ms = position.inMilliseconds; + + int seconds = ms ~/ 1000; + final int hours = seconds ~/ 3600; + seconds = seconds % 3600; + final minutes = seconds ~/ 60; + seconds = seconds % 60; + + final hoursString = hours >= 10 + ? '$hours' + : hours == 0 + ? '00' + : '0$hours'; + + final minutesString = minutes >= 10 + ? '$minutes' + : minutes == 0 + ? '00' + : '0$minutes'; + + final secondsString = seconds >= 10 + ? '$seconds' + : seconds == 0 + ? '00' + : '0$seconds'; + + final formattedTime = + '${hoursString == '00' ? '' : '$hoursString:'}$minutesString:$secondsString'; + + return formattedTime; +} + +bool isLeapYear(DateTime dateTime) { + final year = dateTime.year; + if (year % 4 == 0) { + if (year % 100 == 0) { + if (year % 400 == 0) { + return true; + } else { + return false; + } + } else { + return true; + } + } else { + return false; + } +} + +Widget getDayWidget( + BuildContext context, + int timestamp, + bool smallerTodayFont, +) { + return Container( + padding: const EdgeInsets.fromLTRB(4, 14, 0, 8), + alignment: Alignment.centerLeft, + child: Text( + getDayTitle(timestamp), + style: (getDayTitle(timestamp) == "Today" && !smallerTodayFont) + ? Theme.of(context).textTheme.headlineSmall + : Theme.of(context).textTheme.bodySmall?.copyWith( + fontSize: 16, + fontWeight: FontWeight.w600, + fontFamily: 'Inter-SemiBold', + ), + ), + ); +} + +String getDayTitle(int timestamp) { + final date = DateTime.fromMicrosecondsSinceEpoch(timestamp); + final now = DateTime.now(); + var title = getDayAndMonth(date); + if (date.year == now.year && date.month == now.month) { + if (date.day == now.day) { + title = "Today"; + } else if (date.day == now.day - 1) { + title = "Yesterday"; + } + } + if (date.year != DateTime.now().year) { + title += " ${date.year}"; + } + return title; +} + +String secondsToHHMMSS(int value) { + int h, m, s; + h = value ~/ 3600; + m = ((value - h * 3600)) ~/ 60; + s = value - (h * 3600) - (m * 60); + final String hourLeft = h.toString().length < 2 ? "0$h" : h.toString(); + + final String minuteLeft = m.toString().length < 2 ? "0$m" : m.toString(); + + final String secondsLeft = s.toString().length < 2 ? "0$s" : s.toString(); + + final String result = "$hourLeft:$minuteLeft:$secondsLeft"; + + return result; +} + +bool isValidDate({ + required int day, + required int month, + required int year, +}) { + if (day < 0 || day > 31 || month < 0 || month > 12 || year < 0) { + return false; + } + if (monthWith30Days.contains(month) && day > 30) { + return false; + } + if (month == 2) { + if (day > 29) { + return false; + } + if (day == 29 && year % 4 != 0) { + return false; + } + } + return true; +} + +String formatDate(BuildContext context, DateTime date) { + final now = DateTime.now(); + final difference = now.difference(date); + + if (difference.inMinutes < 1) { + return 'Just now'; + } else if (difference.inMinutes < 60) { + return context.l10n.minutesAgo(difference.inMinutes); + } else if (difference.inDays == 0) { + return context.l10n.hoursAgo(difference.inHours); + } else if (difference.inDays == 1) { + return context.l10n.yesterday; + } else if (difference.inDays < 7) { + return context.l10n.daysAgo(difference.inDays); + } else { + return '${date.day}/${date.month}/${date.year}'; + } +} diff --git a/mobile/apps/locker/lib/utils/file_icon_utils.dart b/mobile/apps/locker/lib/utils/file_icon_utils.dart new file mode 100644 index 0000000000..bc1f238200 --- /dev/null +++ b/mobile/apps/locker/lib/utils/file_icon_utils.dart @@ -0,0 +1,72 @@ +import 'package:flutter/material.dart'; + +class FileIconConfig { + final IconData icon; + final Color color; + final Set extensions; + + const FileIconConfig({ + required this.icon, + required this.color, + required this.extensions, + }); +} + +class FileIconUtils { + // Centralized configuration - change icons and colors here only + static const Map _fileTypeConfigs = { + 'pdf': FileIconConfig( + extensions: {'.pdf'}, + icon: Icons.picture_as_pdf, + color: Colors.red, + ), + 'image': FileIconConfig( + extensions: {'.jpg', '.png', '.heic'}, + icon: Icons.image, + color: Colors.blue, + ), + 'presentation': FileIconConfig( + extensions: {'.pptx'}, + icon: Icons.slideshow, + color: Colors.orange, + ), + 'spreadsheet': FileIconConfig( + extensions: {'.xlsx'}, + icon: Icons.table_chart, + color: Colors.green, + ), + }; + + static const FileIconConfig _defaultConfig = FileIconConfig( + extensions: {}, + icon: Icons.insert_drive_file, + color: Colors.grey, + ); + + static FileIconConfig _getFileConfig(String fileName) { + final lowerFileName = fileName.toLowerCase(); + final lastDotIndex = lowerFileName.lastIndexOf('.'); + + if (lastDotIndex == -1) { + return _defaultConfig; // No extension found + } + + final extension = lowerFileName.substring(lastDotIndex); + + for (final config in _fileTypeConfigs.values) { + if (config.extensions.contains(extension)) { + return config; + } + } + + return _defaultConfig; + } + + static IconData getFileIcon(String fileName) { + return _getFileConfig(fileName).icon; + } + + static Color getFileIconColor(String fileName) { + return _getFileConfig(fileName).color; + } +} diff --git a/mobile/apps/locker/lib/utils/snack_bar_utils.dart b/mobile/apps/locker/lib/utils/snack_bar_utils.dart new file mode 100644 index 0000000000..ab03c69ac1 --- /dev/null +++ b/mobile/apps/locker/lib/utils/snack_bar_utils.dart @@ -0,0 +1,36 @@ +import 'package:ente_ui/theme/ente_theme.dart'; +import 'package:flutter/material.dart'; + +class SnackBarUtils { + static void showInfoSnackBar(BuildContext context, String message) { + _showSnackBar( + context, + message, + backgroundColor: getEnteColorScheme(context).primary500, + ); + } + + static void showWarningSnackBar(BuildContext context, String message) { + _showSnackBar( + context, + message, + backgroundColor: getEnteColorScheme(context).warning500, + ); + } + + static void _showSnackBar( + BuildContext context, + String message, { + Color? backgroundColor, + }) { + if (!context.mounted) return; + + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message), + backgroundColor: + backgroundColor ?? getEnteColorScheme(context).primary500, + ), + ); + } +} diff --git a/mobile/apps/locker/linux/.gitignore b/mobile/apps/locker/linux/.gitignore new file mode 100644 index 0000000000..d3896c9844 --- /dev/null +++ b/mobile/apps/locker/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/mobile/apps/locker/linux/CMakeLists.txt b/mobile/apps/locker/linux/CMakeLists.txt new file mode 100644 index 0000000000..0ce05024f9 --- /dev/null +++ b/mobile/apps/locker/linux/CMakeLists.txt @@ -0,0 +1,145 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "locker") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "io.ente.locker") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/mobile/apps/locker/linux/flutter/CMakeLists.txt b/mobile/apps/locker/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000000..d5bd01648a --- /dev/null +++ b/mobile/apps/locker/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/mobile/apps/locker/linux/flutter/generated_plugin_registrant.cc b/mobile/apps/locker/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..fafe406725 --- /dev/null +++ b/mobile/apps/locker/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,55 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_saver_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); + file_saver_plugin_register_with_registrar(file_saver_registrar); + g_autoptr(FlPluginRegistrar) flutter_local_authentication_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterLocalAuthenticationPlugin"); + flutter_local_authentication_plugin_register_with_registrar(flutter_local_authentication_registrar); + g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); + flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); + g_autoptr(FlPluginRegistrar) open_file_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "OpenFileLinuxPlugin"); + open_file_linux_plugin_register_with_registrar(open_file_linux_registrar); + g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverLinuxPlugin"); + screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar); + g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); + sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); + g_autoptr(FlPluginRegistrar) sodium_libs_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SodiumLibsPlugin"); + sodium_libs_plugin_register_with_registrar(sodium_libs_registrar); + g_autoptr(FlPluginRegistrar) tray_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "TrayManagerPlugin"); + tray_manager_plugin_register_with_registrar(tray_manager_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); +} diff --git a/mobile/apps/locker/linux/flutter/generated_plugin_registrant.h b/mobile/apps/locker/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..e0f0a47bc0 --- /dev/null +++ b/mobile/apps/locker/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/mobile/apps/locker/linux/flutter/generated_plugins.cmake b/mobile/apps/locker/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..a193fb26ff --- /dev/null +++ b/mobile/apps/locker/linux/flutter/generated_plugins.cmake @@ -0,0 +1,35 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + file_saver + flutter_local_authentication + flutter_secure_storage_linux + gtk + open_file_linux + screen_retriever_linux + sentry_flutter + sodium_libs + tray_manager + url_launcher_linux + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/mobile/apps/locker/linux/main.cc b/mobile/apps/locker/linux/main.cc new file mode 100644 index 0000000000..e7c5c54370 --- /dev/null +++ b/mobile/apps/locker/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/mobile/apps/locker/linux/my_application.cc b/mobile/apps/locker/linux/my_application.cc new file mode 100644 index 0000000000..c16008ea45 --- /dev/null +++ b/mobile/apps/locker/linux/my_application.cc @@ -0,0 +1,124 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "locker"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "locker"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/mobile/apps/locker/linux/my_application.h b/mobile/apps/locker/linux/my_application.h new file mode 100644 index 0000000000..72271d5e41 --- /dev/null +++ b/mobile/apps/locker/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/mobile/apps/locker/macos/.gitignore b/mobile/apps/locker/macos/.gitignore new file mode 100644 index 0000000000..746adbb6b9 --- /dev/null +++ b/mobile/apps/locker/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/mobile/apps/locker/macos/Flutter/Flutter-Debug.xcconfig b/mobile/apps/locker/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000000..4b81f9b2d2 --- /dev/null +++ b/mobile/apps/locker/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/apps/locker/macos/Flutter/Flutter-Release.xcconfig b/mobile/apps/locker/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000000..5caa9d1579 --- /dev/null +++ b/mobile/apps/locker/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/mobile/apps/locker/macos/Flutter/GeneratedPluginRegistrant.swift b/mobile/apps/locker/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000000..1eca850c4c --- /dev/null +++ b/mobile/apps/locker/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,50 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import app_links +import device_info_plus +import file_picker +import file_saver +import flutter_inappwebview_macos +import flutter_local_authentication +import flutter_secure_storage_macos +import local_auth_darwin +import open_file_mac +import package_info_plus +import path_provider_foundation +import screen_retriever_macos +import sentry_flutter +import share_plus +import shared_preferences_foundation +import sodium_libs +import sqflite_darwin +import tray_manager +import url_launcher_macos +import window_manager + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) + InAppWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "InAppWebViewFlutterPlugin")) + FlutterLocalAuthenticationPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalAuthenticationPlugin")) + FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin")) + FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) + OpenFilePlugin.register(with: registry.registrar(forPlugin: "OpenFilePlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverMacosPlugin")) + SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) + SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + SodiumLibsPlugin.register(with: registry.registrar(forPlugin: "SodiumLibsPlugin")) + SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) + TrayManagerPlugin.register(with: registry.registrar(forPlugin: "TrayManagerPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) + WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) +} diff --git a/mobile/apps/locker/macos/Podfile b/mobile/apps/locker/macos/Podfile new file mode 100644 index 0000000000..b52666a103 --- /dev/null +++ b/mobile/apps/locker/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.15' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/mobile/apps/locker/macos/Podfile.lock b/mobile/apps/locker/macos/Podfile.lock new file mode 100644 index 0000000000..c6e7122103 --- /dev/null +++ b/mobile/apps/locker/macos/Podfile.lock @@ -0,0 +1,152 @@ +PODS: + - app_links (1.0.0): + - FlutterMacOS + - cupertino_http (0.0.1): + - Flutter + - FlutterMacOS + - device_info_plus (0.0.1): + - FlutterMacOS + - file_picker (0.0.1): + - FlutterMacOS + - file_saver (0.0.1): + - FlutterMacOS + - flutter_inappwebview_macos (0.0.1): + - FlutterMacOS + - OrderedSet (~> 6.0.3) + - flutter_local_authentication (1.2.0): + - FlutterMacOS + - flutter_secure_storage_macos (6.1.3): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS + - objective_c (0.0.1): + - FlutterMacOS + - OrderedSet (6.0.3) + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - screen_retriever_macos (0.0.1): + - FlutterMacOS + - Sentry/HybridSDK (8.46.0) + - sentry_flutter (8.14.2): + - Flutter + - FlutterMacOS + - Sentry/HybridSDK (= 8.46.0) + - share_plus (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sodium_libs (2.2.1): + - FlutterMacOS + - tray_manager (0.0.1): + - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS + - window_manager (0.2.0): + - FlutterMacOS + +DEPENDENCIES: + - app_links (from `Flutter/ephemeral/.symlinks/plugins/app_links/macos`) + - cupertino_http (from `Flutter/ephemeral/.symlinks/plugins/cupertino_http/darwin`) + - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) + - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`) + - flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`) + - flutter_local_authentication (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos`) + - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`) + - FlutterMacOS (from `Flutter/ephemeral`) + - local_auth_darwin (from `Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin`) + - objective_c (from `Flutter/ephemeral/.symlinks/plugins/objective_c/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - screen_retriever_macos (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos`) + - sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`) + - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - sodium_libs (from `Flutter/ephemeral/.symlinks/plugins/sodium_libs/macos`) + - tray_manager (from `Flutter/ephemeral/.symlinks/plugins/tray_manager/macos`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) + +SPEC REPOS: + trunk: + - OrderedSet + - Sentry + +EXTERNAL SOURCES: + app_links: + :path: Flutter/ephemeral/.symlinks/plugins/app_links/macos + cupertino_http: + :path: Flutter/ephemeral/.symlinks/plugins/cupertino_http/darwin + device_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos + file_saver: + :path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos + flutter_inappwebview_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos + flutter_local_authentication: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_authentication/macos + flutter_secure_storage_macos: + :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos + FlutterMacOS: + :path: Flutter/ephemeral + local_auth_darwin: + :path: Flutter/ephemeral/.symlinks/plugins/local_auth_darwin/darwin + objective_c: + :path: Flutter/ephemeral/.symlinks/plugins/objective_c/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + screen_retriever_macos: + :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever_macos/macos + sentry_flutter: + :path: Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos + share_plus: + :path: Flutter/ephemeral/.symlinks/plugins/share_plus/macos + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + sodium_libs: + :path: Flutter/ephemeral/.symlinks/plugins/sodium_libs/macos + tray_manager: + :path: Flutter/ephemeral/.symlinks/plugins/tray_manager/macos + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + window_manager: + :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos + +SPEC CHECKSUMS: + app_links: 86a57d95d4dec830373b8c85c21d1c59a4a5dc21 + cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba + device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f + file_picker: e716a70a9fe5fd9e09ebc922d7541464289443af + file_saver: 44e6fbf666677faf097302460e214e977fdd977b + flutter_inappwebview_macos: bdf207b8f4ebd58e86ae06cd96b147de99a67c9b + flutter_local_authentication: 85674893931e1c9cfa7c9e4f5973cb8c56b018b0 + flutter_secure_storage_macos: c2754d3483d20bb207bb9e5a14f1b8e771abcdb9 + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3 + objective_c: e5f8194456e8fc943e034d1af00510a1bc29c067 + OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 + package_info_plus: 12f1c5c2cfe8727ca46cbd0b26677728972d9a5b + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + screen_retriever_macos: 776e0fa5d42c6163d2bf772d22478df4b302b161 + Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 + sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b + share_plus: 1fa619de8392a4398bfaf176d441853922614e89 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 + sodium_libs: d39bd76697736cb11ce4a0be73b9b4bc64466d6f + tray_manager: 9064e219c56d75c476e46b9a21182087930baf90 + url_launcher_macos: c82c93949963e55b228a30115bd219499a6fe404 + window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + +PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3 + +COCOAPODS: 1.16.2 diff --git a/mobile/apps/locker/macos/Runner.xcodeproj/project.pbxproj b/mobile/apps/locker/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..ae214f8ff9 --- /dev/null +++ b/mobile/apps/locker/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,809 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 0BE25975B65D6BA73D3A1354 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 525AD39957F290EBEA71D275 /* Pods_Runner.framework */; }; + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + C9A78EA7917BBDA4B497AD6E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 858DFB73D44507E1FE278030 /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* locker.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = locker.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 525AD39957F290EBEA71D275 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 54856530D3CF926D9FC7D5AB /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + 56670D826B6B386CB46D62A4 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 6C5BBAB371CF35A643A7437A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 858DFB73D44507E1FE278030 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + A7673CE67E05A584D01EA757 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + F74F319179D2B77563D2E697 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + FFAC0A535D20ED9BDF0ADAD2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C9A78EA7917BBDA4B497AD6E /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0BE25975B65D6BA73D3A1354 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + B360C8315A7BC96A51347C06 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* locker.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + B360C8315A7BC96A51347C06 /* Pods */ = { + isa = PBXGroup; + children = ( + 56670D826B6B386CB46D62A4 /* Pods-Runner.debug.xcconfig */, + FFAC0A535D20ED9BDF0ADAD2 /* Pods-Runner.release.xcconfig */, + 6C5BBAB371CF35A643A7437A /* Pods-Runner.profile.xcconfig */, + F74F319179D2B77563D2E697 /* Pods-RunnerTests.debug.xcconfig */, + 54856530D3CF926D9FC7D5AB /* Pods-RunnerTests.release.xcconfig */, + A7673CE67E05A584D01EA757 /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 525AD39957F290EBEA71D275 /* Pods_Runner.framework */, + 858DFB73D44507E1FE278030 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + 85A4B508E9D300E4FD85782F /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 4C5450204892FC1CE86D7750 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + B035DB2B7066A1CE24DA098C /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* locker.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 4C5450204892FC1CE86D7750 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 85A4B508E9D300E4FD85782F /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + B035DB2B7066A1CE24DA098C /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F74F319179D2B77563D2E697 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/locker.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/locker"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 54856530D3CF926D9FC7D5AB /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/locker.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/locker"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A7673CE67E05A584D01EA757 /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/locker.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/locker"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = 6Z68YJY9Q2; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/mobile/apps/locker/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/apps/locker/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/mobile/apps/locker/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/apps/locker/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/mobile/apps/locker/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..0e55ebac0f --- /dev/null +++ b/mobile/apps/locker/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/macos/Runner.xcworkspace/contents.xcworkspacedata b/mobile/apps/locker/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..21a3cc14c7 --- /dev/null +++ b/mobile/apps/locker/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/mobile/apps/locker/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/mobile/apps/locker/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/mobile/apps/locker/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/mobile/apps/locker/macos/Runner/AppDelegate.swift b/mobile/apps/locker/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000000..8e02df2888 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..a2ec33f19f --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000000000000000000000000000000000000..82b6f9d9a33e198f5747104729e1fcef999772a5 GIT binary patch literal 102994 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ⅅ8MglG&ER{i5lN315cO?EdHNLrg? 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 z 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 Pe7HEdAEg|=c=LY&YVNkY literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000000000000000000000000000000000000..13b35eba55c6dabc3aac36f33d859266c18fa0d0 GIT binary patch literal 5680 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000000000000000000000000000000000000..0a3f5fa40fb3d1e0710331a48de5d256da3f275d GIT binary patch literal 520 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/mobile/apps/locker/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000000000000000000000000000000000000..2f1632cfddf3d9dade342351e627a0a75609fb46 GIT binary patch literal 2218 zcmV;b2vzrqP)Px#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91K%fHv1ONa40RR91KmY&$07g+lumAuE6iGxuRCodHTWf3-RTMruyW6Fu zQYeUM04eX6D5c0FCjKKPrco1(K`<0SL=crI{PC3-^hZU0kQie$gh-5!7z6SH6Q0J% zqot*`H1q{R5fHFYS}dje@;kG=v$L0(yY0?wY2%*c?A&{2?!D*x?m71{of2gv!$5|C z3>qG_BW}7K_yUcT3A5C6QD<+{aq?x;MAUyAiJn#Jv8_zZtQ{P zTRzbL3U9!qVuZzS$xKU10KiW~Bgdcv1-!uAhQxf3a7q+dU6lj?yoO4Lq4TUN4}h{N z*fIM=SS8|C2$(T>w$`t@3Tka!(r!7W`x z-isCVgQD^mG-MJ;XtJuK3V{Vy72GQ83KRWsHU?e*wrhKk=ApIYeDqLi;JI1e zuvv}5^Dc=k7F7?nm3nIw$NVmU-+R>> zyqOR$-2SDpJ}Pt;^RkJytDVXNTsu|mI1`~G7yw`EJR?VkGfNdqK9^^8P`JdtTV&tX4CNcV4 z&N06nZa??Fw1AgQOUSE2AmPE@WO(Fvo`%m`cDgiv(fAeRA%3AGXUbsGw{7Q`cY;1BI#ac3iN$$Hw z0LT0;xc%=q)me?Y*$xI@GRAw?+}>=9D+KTk??-HJ4=A>`V&vKFS75@MKdSF1JTq{S zc1!^8?YA|t+uKigaq!sT;Z!&0F2=k7F0PIU;F$leJLaw2UI6FL^w}OG&!;+b%ya1c z1n+6-inU<0VM-Y_s5iTElq)ThyF?StVcebpGI znw#+zLx2@ah{$_2jn+@}(zJZ{+}_N9BM;z)0yr|gF-4=Iyu@hI*Lk=-A8f#bAzc9f z`Kd6K--x@t04swJVC3JK1cHY-Hq+=|PN-VO;?^_C#;coU6TDP7Bt`;{JTG;!+jj(` zw5cLQ-(Cz-Tlb`A^w7|R56Ce;Wmr0)$KWOUZ6ai0PhzPeHwdl0H(etP zUV`va_i0s-4#DkNM8lUlqI7>YQLf)(lz9Q3Uw`)nc(z3{m5ZE77Ul$V%m)E}3&8L0 z-XaU|eB~Is08eORPk;=<>!1w)Kf}FOVS2l&9~A+@R#koFJ$Czd%Y(ENTV&A~U(IPI z;UY+gf+&6ioZ=roly<0Yst8ck>(M=S?B-ys3mLdM&)ex!hbt+ol|T6CTS+Sc0jv(& z7ijdvFwBq;0a{%3GGwkDKTeG`b+lyj0jjS1OMkYnepCdoosNY`*zmBIo*981BU%%U z@~$z0V`OVtIbEx5pa|Tct|Lg#ZQf5OYMUMRD>Wdxm5SAqV2}3!ceE-M2 z@O~lQ0OiKQp}o9I;?uxCgYVV?FH|?Riri*U$Zi_`V2eiA>l zdSm6;SEm6#T+SpcE8Ro_f2AwxzI z44hfe^WE3!h@W3RDyA_H440cpmYkv*)6m1XazTqw%=E5Xv7^@^^T7Q2wxr+Z2kVYr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/apps/locker/macos/Runner/Configs/AppInfo.xcconfig b/mobile/apps/locker/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000000..1463149715 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,17 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = locker + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = io.ente.locker + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2025 Ente Technologies, Inc. All rights reserved. + +// The minimum macOS deployment target +MACOSX_DEPLOYMENT_TARGET = 10.15 diff --git a/mobile/apps/locker/macos/Runner/Configs/Debug.xcconfig b/mobile/apps/locker/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000000..36b0fd9464 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/mobile/apps/locker/macos/Runner/Configs/Release.xcconfig b/mobile/apps/locker/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000000..dff4f49561 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/mobile/apps/locker/macos/Runner/Configs/Warnings.xcconfig b/mobile/apps/locker/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000000..42bcbf4780 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/mobile/apps/locker/macos/Runner/DebugProfile.entitlements b/mobile/apps/locker/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000000..7c098966de --- /dev/null +++ b/mobile/apps/locker/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + com.apple.security.files.user-selected.read-only + + keychain-access-groups + + + diff --git a/mobile/apps/locker/macos/Runner/Info.plist b/mobile/apps/locker/macos/Runner/Info.plist new file mode 100644 index 0000000000..4789daa6a4 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/mobile/apps/locker/macos/Runner/MainFlutterWindow.swift b/mobile/apps/locker/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000000..3cc05eb234 --- /dev/null +++ b/mobile/apps/locker/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/mobile/apps/locker/macos/Runner/Release.entitlements b/mobile/apps/locker/macos/Runner/Release.entitlements new file mode 100644 index 0000000000..1de9b453af --- /dev/null +++ b/mobile/apps/locker/macos/Runner/Release.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + com.apple.security.network.client + + keychain-access-groups + + + diff --git a/mobile/apps/locker/macos/RunnerTests/RunnerTests.swift b/mobile/apps/locker/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000000..61f3bd1fc5 --- /dev/null +++ b/mobile/apps/locker/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import Cocoa +import FlutterMacOS +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/mobile/apps/locker/pubspec.lock b/mobile/apps/locker/pubspec.lock new file mode 100644 index 0000000000..a69b237c67 --- /dev/null +++ b/mobile/apps/locker/pubspec.lock @@ -0,0 +1,1471 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + adaptive_theme: + dependency: "direct main" + description: + name: adaptive_theme + sha256: f4ee609b464e5efc68131d9d15ba9aa1de4e3b5ede64be17781c6e19a52d637d + url: "https://pub.dev" + source: hosted + version: "3.6.0" + app_links: + dependency: transitive + description: + name: app_links + sha256: "85ed8fc1d25a76475914fff28cc994653bd900bc2c26e4b57a49e097febb54ba" + url: "https://pub.dev" + source: hosted + version: "6.4.0" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.dev" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.dev" + source: hosted + version: "1.0.4" + archive: + dependency: transitive + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.dev" + source: hosted + version: "4.0.7" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" + async: + dependency: transitive + description: + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bip39: + dependency: transitive + description: + name: bip39 + sha256: de1ee27ebe7d96b84bb3a04a4132a0a3007dcdd5ad27dd14aa87a29d97c45edc + url: "https://pub.dev" + source: hosted + version: "1.0.6" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: "direct main" + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + cronet_http: + dependency: transitive + description: + name: cronet_http + sha256: "5ed075c59b2d4bd43af4e73d906b8082e98ecd2af9c625327370ef28361bf635" + url: "https://pub.dev" + source: hosted + version: "1.3.4" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.dev" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + cupertino_http: + dependency: transitive + description: + name: cupertino_http + sha256: "8fb9e2c36d0732d9d96abd76683406b57e78a2514e27c962e0c603dbe6f2e3f8" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + device_info_plus: + dependency: transitive + description: + name: device_info_plus + sha256: "77f757b789ff68e4eaf9c56d1752309bd9f7ad557cb105b938a7f8eb89e59110" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + device_info_plus_platform_interface: + dependency: transitive + description: + name: device_info_plus_platform_interface + sha256: "0b04e02b30791224b31969eb1b50d723498f402971bff3630bca2ba839bd1ed2" + url: "https://pub.dev" + source: hosted + version: "7.0.2" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + dotted_border: + dependency: transitive + description: + name: dotted_border + sha256: "99b091ec6891ba0c5331fdc2b502993c7c108f898995739a73c6845d71dad70c" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + email_validator: + dependency: "direct main" + description: + name: email_validator + sha256: b19aa5d92fdd76fbc65112060c94d45ba855105a28bb6e462de7ff03b12fa1fb + url: "https://pub.dev" + source: hosted + version: "3.0.0" + ente_accounts: + dependency: "direct main" + description: + path: "../../packages/accounts" + relative: true + source: path + version: "1.0.0" + ente_base: + dependency: "direct main" + description: + path: "../../packages/base" + relative: true + source: path + version: "1.0.0" + ente_configuration: + dependency: "direct main" + description: + path: "../../packages/configuration" + relative: true + source: path + version: "1.0.0" + ente_crypto_dart: + dependency: "direct main" + description: + path: "." + ref: HEAD + resolved-ref: f91e1545f8263df127762240c4da54a0c42835b2 + url: "https://github.com/ente-io/ente_crypto_dart.git" + source: git + version: "1.0.0" + ente_events: + dependency: "direct main" + description: + path: "../../packages/events" + relative: true + source: path + version: "1.0.0" + ente_lock_screen: + dependency: "direct main" + description: + path: "../../packages/lock_screen" + relative: true + source: path + version: "1.0.0" + ente_logging: + dependency: "direct main" + description: + path: "../../packages/logging" + relative: true + source: path + version: "1.0.0" + ente_network: + dependency: "direct main" + description: + path: "../../packages/network" + relative: true + source: path + version: "1.0.0" + ente_strings: + dependency: "direct main" + description: + path: "../../packages/strings" + relative: true + source: path + version: "1.0.0" + ente_ui: + dependency: "direct main" + description: + path: "../../packages/ui" + relative: true + source: path + version: "1.0.0" + ente_utils: + dependency: "direct main" + description: + path: "../../packages/utils" + relative: true + source: path + version: "1.0.0" + event_bus: + dependency: "direct main" + description: + name: event_bus + sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" + url: "https://pub.dev" + source: hosted + version: "2.0.1" + expandable: + dependency: transitive + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + fast_base58: + dependency: "direct main" + description: + name: fast_base58 + sha256: "611f65633b734f27a850b51371b3eba993a5165650e12e8e7b02959f3768ba06" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" + url: "https://pub.dev" + source: hosted + version: "2.1.3" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + sha256: ef9908739bdd9c476353d6adff72e88fd00c625f5b959ae23f7567bd5137db0a + url: "https://pub.dev" + source: hosted + version: "10.2.0" + file_saver: + dependency: transitive + description: + name: file_saver + sha256: "9d93db09bd4da9e43238f9dd485360fc51a5c138eea5ef5f407ec56e58079ac0" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_animate: + dependency: transitive + description: + name: flutter_animate + sha256: "7befe2d3252728afb77aecaaea1dec88a89d35b9b1d2eea6d04479e8af9117b5" + url: "https://pub.dev" + source: hosted + version: "4.5.2" + flutter_displaymode: + dependency: "direct main" + description: + name: flutter_displaymode + sha256: "42c5e9abd13d28ed74f701b60529d7f8416947e58256e6659c5550db719c57ef" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_email_sender: + dependency: transitive + description: + name: flutter_email_sender + sha256: d39eb5e91358fc19ec4050da69accec21f9d5b2b6bcf188aa246327b6ca2352c + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_inappwebview: + dependency: transitive + description: + name: flutter_inappwebview + sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5" + url: "https://pub.dev" + source: hosted + version: "6.1.5" + flutter_inappwebview_android: + dependency: transitive + description: + name: flutter_inappwebview_android + sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba" + url: "https://pub.dev" + source: hosted + version: "1.1.3" + flutter_inappwebview_internal_annotations: + dependency: transitive + description: + name: flutter_inappwebview_internal_annotations + sha256: "787171d43f8af67864740b6f04166c13190aa74a1468a1f1f1e9ee5b90c359cd" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + flutter_inappwebview_ios: + dependency: transitive + description: + name: flutter_inappwebview_ios + sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_macos: + dependency: transitive + description: + name: flutter_inappwebview_macos + sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_platform_interface: + dependency: transitive + description: + name: flutter_inappwebview_platform_interface + sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500 + url: "https://pub.dev" + source: hosted + version: "1.3.0+1" + flutter_inappwebview_web: + dependency: transitive + description: + name: flutter_inappwebview_web + sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_inappwebview_windows: + dependency: transitive + description: + name: flutter_inappwebview_windows + sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055" + url: "https://pub.dev" + source: hosted + version: "0.6.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + flutter_local_authentication: + dependency: "direct main" + description: + path: "." + ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + resolved-ref: "1ac346a04592a05fd75acccf2e01fa3c7e955d96" + url: "https://github.com/eaceto/flutter_local_authentication" + source: git + version: "1.2.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + sha256: "1c2b787f99bdca1f3718543f81d38aa1b124817dfeb9fb196201bea85b6134bf" + url: "https://pub.dev" + source: hosted + version: "2.0.26" + flutter_secure_storage: + dependency: transitive + description: + name: flutter_secure_storage + sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea" + url: "https://pub.dev" + source: hosted + version: "9.2.4" + flutter_secure_storage_linux: + dependency: transitive + description: + name: flutter_secure_storage_linux + sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + flutter_secure_storage_macos: + dependency: transitive + description: + name: flutter_secure_storage_macos + sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247" + url: "https://pub.dev" + source: hosted + version: "3.1.3" + flutter_secure_storage_platform_interface: + dependency: transitive + description: + name: flutter_secure_storage_platform_interface + sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8 + url: "https://pub.dev" + source: hosted + version: "1.1.2" + flutter_secure_storage_web: + dependency: transitive + description: + name: flutter_secure_storage_web + sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + flutter_secure_storage_windows: + dependency: transitive + description: + name: flutter_secure_storage_windows + sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + flutter_shaders: + dependency: transitive + description: + name: flutter_shaders + sha256: "34794acadd8275d971e02df03afee3dee0f98dbfb8c4837082ad0034f612a3e2" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + fluttertoast: + dependency: transitive + description: + name: fluttertoast + sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1" + url: "https://pub.dev" + source: hosted + version: "8.2.12" + freezed_annotation: + dependency: transitive + description: + name: freezed_annotation + sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + url: "https://pub.dev" + source: hosted + version: "2.4.4" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" + hex: + dependency: transitive + description: + name: hex + sha256: "4e7cd54e4b59ba026432a6be2dd9d96e4c5205725194997193bf871703b82c4a" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + html: + dependency: transitive + description: + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" + url: "https://pub.dev" + source: hosted + version: "0.15.6" + http: + dependency: "direct main" + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + http_profile: + dependency: transitive + description: + name: http_profile + sha256: "7e679e355b09aaee2ab5010915c932cce3f2d1c11c3b2dc177891687014ffa78" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + io: + dependency: "direct main" + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + jni: + dependency: transitive + description: + name: jni + sha256: "459727a9daf91bdfb39b014cf3c186cf77f0136124a274ac83c186e12262ac4e" + url: "https://pub.dev" + source: hosted + version: "0.12.2" + js: + dependency: transitive + description: + name: js + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 + url: "https://pub.dev" + source: hosted + version: "0.6.7" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" + url: "https://pub.dev" + source: hosted + version: "10.0.9" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + listen_sharing_intent: + dependency: "direct main" + description: + name: listen_sharing_intent + sha256: df12e6c78666018985c39df494fc12412ece6510c38c79fe1230f2abef0965fa + url: "https://pub.dev" + source: hosted + version: "1.9.2" + local_auth: + dependency: transitive + description: + name: local_auth + sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + local_auth_android: + dependency: transitive + description: + name: local_auth_android + sha256: "8bba79f4f0f7bc812fce2ca20915d15618c37721246ba6c3ef2aa7a763a90cf2" + url: "https://pub.dev" + source: hosted + version: "1.0.47" + local_auth_darwin: + dependency: transitive + description: + name: local_auth_darwin + sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2" + url: "https://pub.dev" + source: hosted + version: "1.4.3" + local_auth_platform_interface: + dependency: transitive + description: + name: local_auth_platform_interface + sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a" + url: "https://pub.dev" + source: hosted + version: "1.0.10" + local_auth_windows: + dependency: transitive + description: + name: local_auth_windows + sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 + url: "https://pub.dev" + source: hosted + version: "1.0.11" + logging: + dependency: "direct main" + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + menu_base: + dependency: transitive + description: + name: menu_base + sha256: "820368014a171bd1241030278e6c2617354f492f5c703d7b7d4570a6b8b84405" + url: "https://pub.dev" + source: hosted + version: "0.1.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + modal_bottom_sheet: + dependency: transitive + description: + name: modal_bottom_sheet + sha256: eac66ef8cb0461bf069a38c5eb0fa728cee525a531a8304bd3f7b2185407c67e + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_dio_adapter: + dependency: transitive + description: + name: native_dio_adapter + sha256: "7420bc9517b2abe09810199a19924617b45690a44ecfb0616ac9babc11875c03" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + objective_c: + dependency: transitive + description: + name: objective_c + sha256: "9f034ba1eeca53ddb339bc8f4813cb07336a849cd735559b60cdc068ecce2dc7" + url: "https://pub.dev" + source: hosted + version: "7.1.0" + open_file: + dependency: "direct main" + description: + name: open_file + sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e + url: "https://pub.dev" + source: hosted + version: "3.5.10" + open_file_android: + dependency: transitive + description: + name: open_file_android + sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa" + url: "https://pub.dev" + source: hosted + version: "1.0.6" + open_file_ios: + dependency: transitive + description: + name: open_file_ios + sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_linux: + dependency: transitive + description: + name: open_file_linux + sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550 + url: "https://pub.dev" + source: hosted + version: "0.0.5" + open_file_mac: + dependency: transitive + description: + name: open_file_mac + sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_platform_interface: + dependency: transitive + description: + name: open_file_platform_interface + sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757" + url: "https://pub.dev" + source: hosted + version: "1.0.3" + open_file_web: + dependency: transitive + description: + name: open_file_web + sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f + url: "https://pub.dev" + source: hosted + version: "0.0.4" + open_file_windows: + dependency: transitive + description: + name: open_file_windows + sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875 + url: "https://pub.dev" + source: hosted + version: "0.0.3" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" + package_info_plus: + dependency: "direct main" + description: + name: package_info_plus + sha256: "7976bfe4c583170d6cdc7077e3237560b364149fcd268b5f53d95a991963b191" + url: "https://pub.dev" + source: hosted + version: "8.3.0" + package_info_plus_platform_interface: + dependency: transitive + description: + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + password_strength: + dependency: transitive + description: + name: password_strength + sha256: "0e51e3d864e37873a1347e658147f88b66e141ee36c58e19828dc5637961e1ce" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + path: + dependency: "direct main" + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" + url: "https://pub.dev" + source: hosted + version: "2.2.15" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + pinput: + dependency: transitive + description: + name: pinput + sha256: "8a73be426a91fefec90a7f130763ca39772d547e92f19a827cf4aa02e323d35a" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.dev" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + pointycastle: + dependency: transitive + description: + name: pointycastle + sha256: "4be0097fcf3fd3e8449e53730c631200ebc7b88016acecab2b0da2f0149222fe" + url: "https://pub.dev" + source: hosted + version: "3.9.1" + posix: + dependency: transitive + description: + name: posix + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + privacy_screen: + dependency: transitive + description: + name: privacy_screen + sha256: "2856e3a3ed082061a5cd2a1518f1ce6367c55916fb75e5db72e5983033a1ca54" + url: "https://pub.dev" + source: hosted + version: "0.0.8" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + sentry: + dependency: transitive + description: + name: sentry + sha256: "599701ca0693a74da361bc780b0752e1abc98226cf5095f6b069648116c896bb" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + sentry_flutter: + dependency: transitive + description: + name: sentry_flutter + sha256: "5ba2cf40646a77d113b37a07bd69f61bb3ec8a73cbabe5537b05a7c89d2656f8" + url: "https://pub.dev" + source: hosted + version: "8.14.2" + share_plus: + dependency: transitive + description: + name: share_plus + sha256: b2961506569e28948d75ec346c28775bb111986bb69dc6a20754a457e3d97fa0 + url: "https://pub.dev" + source: hosted + version: "11.0.0" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: "1032d392bc5d2095a77447a805aa3f804d2ae6a4d5eef5e6ebb3bd94c1bc19ef" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "9f9f3d372d4304723e6136663bb291c0b93f5e4c8a4a6314347f481a33bda2b1" + url: "https://pub.dev" + source: hosted + version: "2.4.7" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shortid: + dependency: transitive + description: + name: shortid + sha256: d0b40e3dbb50497dad107e19c54ca7de0d1a274eb9b4404991e443dadb9ebedb + url: "https://pub.dev" + source: hosted + version: "0.1.2" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + sodium: + dependency: transitive + description: + name: sodium + sha256: d9830a388e37c82891888e64cfd4c6764fa3ac716bed80ac6eab89ee42c3cd76 + url: "https://pub.dev" + source: hosted + version: "2.3.1+1" + sodium_libs: + dependency: transitive + description: + name: sodium_libs + sha256: aa764acd6ccc6113e119c2d99471aeeb4637a9a501639549b297d3a143ff49b3 + url: "https://pub.dev" + source: hosted + version: "2.2.1+6" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.dev" + source: hosted + version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: "direct main" + description: + name: sqflite + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + url: "https://pub.dev" + source: hosted + version: "2.5.5" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + step_progress_indicator: + dependency: transitive + description: + name: step_progress_indicator + sha256: b51bb1fcfc78454359f0658c5a2c21548c3825ebf76e826308e9ca10f383bbb8 + url: "https://pub.dev" + source: hosted + version: "1.0.2" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + styled_text: + dependency: "direct main" + description: + name: styled_text + sha256: fd624172cf629751b4f171dd0ecf9acf02a06df3f8a81bb56c0caa4f1df706c3 + url: "https://pub.dev" + source: hosted + version: "8.1.0" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" + url: "https://pub.dev" + source: hosted + version: "3.3.0+3" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + url: "https://pub.dev" + source: hosted + version: "0.7.4" + tray_manager: + dependency: "direct main" + description: + name: tray_manager + sha256: ad18c4cd73003097d182884bacb0578ad2865f3ab842a0ad00f6d043ed49eaf0 + url: "https://pub.dev" + source: hosted + version: "0.5.0" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + ua_client_hints: + dependency: transitive + description: + name: ua_client_hints + sha256: "1b8759a46bfeab355252881df27f2604c01bded86aa2b578869fb1b638b23118" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + url_launcher: + dependency: transitive + description: + name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" + url: "https://pub.dev" + source: hosted + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "6fc2f56536ee873eeb867ad176ae15f304ccccc357848b351f6f0d8d4a40d193" + url: "https://pub.dev" + source: hosted + version: "6.3.14" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.dev" + source: hosted + version: "6.3.3" + url_launcher_linux: + dependency: transitive + description: + name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e" + url: "https://pub.dev" + source: hosted + version: "2.3.3" + url_launcher_windows: + dependency: transitive + description: + name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 + url: "https://pub.dev" + source: hosted + version: "15.0.0" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + win32: + dependency: transitive + description: + name: win32 + sha256: daf97c9d80197ed7b619040e86c8ab9a9dad285e7671ee7390f9180cc828a51e + url: "https://pub.dev" + source: hosted + version: "5.10.1" + win32_registry: + dependency: transitive + description: + name: win32_registry + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" + url: "https://pub.dev" + source: hosted + version: "1.1.5" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xmlstream: + dependency: transitive + description: + name: xmlstream + sha256: cfc14e3f256997897df9481ae630d94c2d85ada5187ebeb868bb1aabc2c977b4 + url: "https://pub.dev" + source: hosted + version: "1.1.1" +sdks: + dart: ">=3.7.2 <4.0.0" + flutter: ">=3.24.0" diff --git a/mobile/apps/locker/pubspec.yaml b/mobile/apps/locker/pubspec.yaml new file mode 100644 index 0000000000..1e1197b001 --- /dev/null +++ b/mobile/apps/locker/pubspec.yaml @@ -0,0 +1,87 @@ +name: locker +description: "Ente Locker – Safe space for your important documents" +publish_to: "none" +version: 0.1.0 + +environment: + sdk: ^3.6.0 + +dependencies: + adaptive_theme: ^3.6.0 + collection: ^1.18.0 + dio: ^5.8.0+1 + email_validator: ^3.0.0 + ente_accounts: + path: ../../packages/accounts + ente_base: + path: ../../packages/base + ente_configuration: + path: ../../packages/configuration + ente_crypto_dart: + git: + url: https://github.com/ente-io/ente_crypto_dart.git + ente_events: + path: ../../packages/events + ente_lock_screen: + path: ../../packages/lock_screen + ente_logging: + path: ../../packages/logging + ente_network: + path: ../../packages/network + ente_strings: + path: ../../packages/strings + ente_ui: + path: ../../packages/ui + ente_utils: + path: ../../packages/utils + event_bus: ^2.0.1 + fast_base58: ^0.2.1 + file_picker: ^10.2.0 + flutter: + sdk: flutter + flutter_displaymode: ^0.6.0 + flutter_local_authentication: + git: + url: https://github.com/eaceto/flutter_local_authentication + ref: 1ac346a04592a05fd75acccf2e01fa3c7e955d96 + flutter_localizations: + sdk: flutter + http: ^1.4.0 + intl: ^0.20.2 + io: ^1.0.5 + listen_sharing_intent: ^1.9.2 + logging: ^1.3.0 + open_file: ^3.5.10 + package_info_plus: ^8.3.0 + path: ^1.9.0 + path_provider: ^2.1.5 + shared_preferences: ^2.5.3 + sqflite: ^2.4.1 + styled_text: ^8.1.0 + tray_manager: ^0.5.0 + uuid: ^4.5.1 + window_manager: ^0.5.0 + +dev_dependencies: + flutter_lints: ^4.0.0 + flutter_test: + sdk: flutter + +flutter: + uses-material-design: true + generate: true + + assets: + - assets/ + + fonts: + - family: Inter + fonts: + - asset: assets/fonts/Inter-Regular.ttf + - asset: assets/fonts/Inter-Medium.ttf + - asset: assets/fonts/Inter-Light.ttf + - asset: assets/fonts/Inter-SemiBold.ttf + - asset: assets/fonts/Inter-Bold.ttf + - family: Montserrat + fonts: + - asset: assets/fonts/Montserrat-Bold.ttf diff --git a/mobile/apps/locker/web/favicon.png b/mobile/apps/locker/web/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8aaa46ac1ae21512746f852a42ba87e4165dfdd1 GIT binary patch literal 917 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|I14-?iy0X7 zltGxWVyS%@P(fs7NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xph&xL%(-1c06+^uR^q@XSM&D4+Kp$>4P^%3{)XKjo zGZknv$b36P8?Z_gF{nK@`XI}Z90TzwSQO}0J1!f2c(B=V`5aP@1P1a|PZ!4!3&Gl8 zTYqUsf!gYFyJnXpu0!n&N*SYAX-%d(5gVjrHJWqXQshj@!Zm{!01WsQrH~9=kTxW#6SvuapgMqt>$=j#%eyGrQzr zP{L-3gsMA^$I1&gsBAEL+vxi1*Igl=8#8`5?A-T5=z-sk46WA1IUT)AIZHx1rdUrf zVJrJn<74DDw`j)Ki#gt}mIT-Q`XRa2-jQXQoI%w`nb|XblvzK${ZzlV)m-XcwC(od z71_OEC5Bt9GEXosOXaPTYOia#R4ID2TiU~`zVMl08TV_C%DnU4^+HE>9(CE4D6?Fz oujB08i7adh9xk7*FX66dWH6F5TM;?E2b5PlUHx3vIVCg!0Dx9vYXATM literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/web/icons/Icon-192.png b/mobile/apps/locker/web/icons/Icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b749bfef07473333cf1dd31e9eed89862a5d52aa GIT binary patch literal 5292 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/web/icons/Icon-512.png b/mobile/apps/locker/web/icons/Icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..88cfd48dff1169879ba46840804b412fe02fefd6 GIT binary patch literal 8252 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/web/icons/Icon-maskable-192.png b/mobile/apps/locker/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9b4d76e525556d5d89141648c724331630325d GIT binary patch literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/web/icons/Icon-maskable-512.png b/mobile/apps/locker/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000000000000000000000000000000000000..d69c56691fbdb0b7efa65097c7cc1edac12a6d3e GIT binary patch literal 20998 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 MPgg&ebxsLQ025IeI{*Lx literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/web/index.html b/mobile/apps/locker/web/index.html new file mode 100644 index 0000000000..ec3b5634e6 --- /dev/null +++ b/mobile/apps/locker/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + Ente Locker + + + + + + diff --git a/mobile/apps/locker/web/manifest.json b/mobile/apps/locker/web/manifest.json new file mode 100644 index 0000000000..3edea1b115 --- /dev/null +++ b/mobile/apps/locker/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "Ente Locker", + "short_name": "Ente Locker", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/mobile/apps/locker/windows/.gitignore b/mobile/apps/locker/windows/.gitignore new file mode 100644 index 0000000000..d492d0d98c --- /dev/null +++ b/mobile/apps/locker/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/mobile/apps/locker/windows/CMakeLists.txt b/mobile/apps/locker/windows/CMakeLists.txt new file mode 100644 index 0000000000..5c2121d6e0 --- /dev/null +++ b/mobile/apps/locker/windows/CMakeLists.txt @@ -0,0 +1,108 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(locker LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "locker") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(VERSION 3.14...3.25) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/mobile/apps/locker/windows/flutter/CMakeLists.txt b/mobile/apps/locker/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000000..903f4899d6 --- /dev/null +++ b/mobile/apps/locker/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/mobile/apps/locker/windows/flutter/generated_plugin_registrant.cc b/mobile/apps/locker/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000000..725202fc0a --- /dev/null +++ b/mobile/apps/locker/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,50 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + FileSaverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSaverPlugin")); + FlutterInappwebviewWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterInappwebviewWindowsPluginCApi")); + FlutterLocalAuthenticationPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterLocalAuthenticationPluginCApi")); + FlutterSecureStorageWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin")); + LocalAuthPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("LocalAuthPlugin")); + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); + SentryFlutterPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SentryFlutterPlugin")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + SodiumLibsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SodiumLibsPluginCApi")); + TrayManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("TrayManagerPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); +} diff --git a/mobile/apps/locker/windows/flutter/generated_plugin_registrant.h b/mobile/apps/locker/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000000..dc139d85a9 --- /dev/null +++ b/mobile/apps/locker/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/mobile/apps/locker/windows/flutter/generated_plugins.cmake b/mobile/apps/locker/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000000..ddd64f7ebe --- /dev/null +++ b/mobile/apps/locker/windows/flutter/generated_plugins.cmake @@ -0,0 +1,37 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + app_links + file_saver + flutter_inappwebview_windows + flutter_local_authentication + flutter_secure_storage_windows + local_auth_windows + screen_retriever_windows + sentry_flutter + share_plus + sodium_libs + tray_manager + url_launcher_windows + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST + jni +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/mobile/apps/locker/windows/runner/CMakeLists.txt b/mobile/apps/locker/windows/runner/CMakeLists.txt new file mode 100644 index 0000000000..394917c053 --- /dev/null +++ b/mobile/apps/locker/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/mobile/apps/locker/windows/runner/Runner.rc b/mobile/apps/locker/windows/runner/Runner.rc new file mode 100644 index 0000000000..793fab1e4e --- /dev/null +++ b/mobile/apps/locker/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.ente" "\0" + VALUE "FileDescription", "locker" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "locker" "\0" + VALUE "LegalCopyright", "Copyright (C) 2025 Ente Technologies, Inc. All rights reserved." "\0" + VALUE "OriginalFilename", "locker.exe" "\0" + VALUE "ProductName", "locker" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/mobile/apps/locker/windows/runner/flutter_window.cpp b/mobile/apps/locker/windows/runner/flutter_window.cpp new file mode 100644 index 0000000000..955ee3038f --- /dev/null +++ b/mobile/apps/locker/windows/runner/flutter_window.cpp @@ -0,0 +1,71 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + // Flutter can complete the first frame before the "show window" callback is + // registered. The following call ensures a frame is pending to ensure the + // window is shown. It is a no-op if the first frame hasn't completed yet. + flutter_controller_->ForceRedraw(); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/mobile/apps/locker/windows/runner/flutter_window.h b/mobile/apps/locker/windows/runner/flutter_window.h new file mode 100644 index 0000000000..6da0652f05 --- /dev/null +++ b/mobile/apps/locker/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/mobile/apps/locker/windows/runner/main.cpp b/mobile/apps/locker/windows/runner/main.cpp new file mode 100644 index 0000000000..aa0a106057 --- /dev/null +++ b/mobile/apps/locker/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"locker", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/mobile/apps/locker/windows/runner/resource.h b/mobile/apps/locker/windows/runner/resource.h new file mode 100644 index 0000000000..66a65d1e4a --- /dev/null +++ b/mobile/apps/locker/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/mobile/apps/locker/windows/runner/resources/app_icon.ico b/mobile/apps/locker/windows/runner/resources/app_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c04e20caf6370ebb9253ad831cc31de4a9c965f6 GIT binary patch literal 33772 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 literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/windows/runner/runner.exe.manifest b/mobile/apps/locker/windows/runner/runner.exe.manifest new file mode 100644 index 0000000000..153653e8d6 --- /dev/null +++ b/mobile/apps/locker/windows/runner/runner.exe.manifest @@ -0,0 +1,14 @@ + + + + + PerMonitorV2 + + + + + + + + + diff --git a/mobile/apps/locker/windows/runner/utils.cpp b/mobile/apps/locker/windows/runner/utils.cpp new file mode 100644 index 0000000000..3a0b46511a --- /dev/null +++ b/mobile/apps/locker/windows/runner/utils.cpp @@ -0,0 +1,65 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + unsigned int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr) + -1; // remove the trailing null character + int input_length = (int)wcslen(utf16_string); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + input_length, utf8_string.data(), target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/mobile/apps/locker/windows/runner/utils.h b/mobile/apps/locker/windows/runner/utils.h new file mode 100644 index 0000000000..3879d54755 --- /dev/null +++ b/mobile/apps/locker/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/mobile/apps/locker/windows/runner/win32_window.cpp b/mobile/apps/locker/windows/runner/win32_window.cpp new file mode 100644 index 0000000000..60608d0fe5 --- /dev/null +++ b/mobile/apps/locker/windows/runner/win32_window.cpp @@ -0,0 +1,288 @@ +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registrar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/mobile/apps/locker/windows/runner/win32_window.h b/mobile/apps/locker/windows/runner/win32_window.h new file mode 100644 index 0000000000..e901dde684 --- /dev/null +++ b/mobile/apps/locker/windows/runner/win32_window.h @@ -0,0 +1,102 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From 8bacf8ff93694e99e4bf933098061b888701e3ca Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 19:09:44 +0530 Subject: [PATCH 153/164] fix: widget tap not working on iOS by giving preferences to init --- mobile/apps/photos/lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/apps/photos/lib/main.dart b/mobile/apps/photos/lib/main.dart index db7aa5d1b8..152f1d98e1 100644 --- a/mobile/apps/photos/lib/main.dart +++ b/mobile/apps/photos/lib/main.dart @@ -224,6 +224,8 @@ Future _init(bool isBackground, {String via = ''}) async { Computer.shared().turnOn(workersCount: 4).ignore(); CryptoUtil.init(); + HomeWidgetService.instance.init(preferences); + _logger.info("Lockscreen init $tlog"); unawaited(LockScreenSettings.instance.init(preferences)); @@ -269,8 +271,6 @@ Future _init(bool isBackground, {String via = ''}) async { await SyncService.instance.init(preferences); _logger.info("SyncService init done $tlog"); - HomeWidgetService.instance.init(preferences); - if (!isBackground) { await _scheduleFGHomeWidgetSync(); } From 3b7600ae7b43abe702e33f3fabced71cdd7cabaf Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 19:09:57 +0530 Subject: [PATCH 154/164] chore: update lock files --- mobile/apps/photos/ios/Podfile.lock | 15 +++++++++++++++ .../photos/ios/Runner.xcodeproj/project.pbxproj | 4 ++++ 2 files changed, 19 insertions(+) diff --git a/mobile/apps/photos/ios/Podfile.lock b/mobile/apps/photos/ios/Podfile.lock index a152ef181c..3a8b2d74df 100644 --- a/mobile/apps/photos/ios/Podfile.lock +++ b/mobile/apps/photos/ios/Podfile.lock @@ -12,6 +12,8 @@ PODS: - Flutter - device_info_plus (0.0.1): - Flutter + - emoji_picker_flutter (0.0.1): + - Flutter - ffmpeg_kit_custom (6.0.3) - ffmpeg_kit_flutter (6.0.3): - ffmpeg_kit_custom @@ -179,6 +181,8 @@ PODS: - PromisesObjC (2.4.0) - receive_sharing_intent (1.8.1): - Flutter + - rust_lib_photos (0.0.1): + - Flutter - SDWebImage (5.21.1): - SDWebImage/Core (= 5.21.1) - SDWebImage/Core (5.21.1) @@ -228,6 +232,8 @@ PODS: - Flutter - url_launcher_ios (0.0.1): - Flutter + - vibration (1.7.5): + - Flutter - video_player_avfoundation (0.0.1): - Flutter - FlutterMacOS @@ -248,6 +254,7 @@ DEPENDENCIES: - cupertino_http (from `.symlinks/plugins/cupertino_http/darwin`) - dart_ui_isolate (from `.symlinks/plugins/dart_ui_isolate/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) + - emoji_picker_flutter (from `.symlinks/plugins/emoji_picker_flutter/ios`) - ffmpeg_kit_flutter (from `.symlinks/plugins/ffmpeg_kit_flutter/ios`) - file_saver (from `.symlinks/plugins/file_saver/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) @@ -294,6 +301,7 @@ DEPENDENCIES: - thermal (from `.symlinks/plugins/thermal/ios`) - ua_client_hints (from `.symlinks/plugins/ua_client_hints/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - vibration (from `.symlinks/plugins/vibration/ios`) - video_player_avfoundation (from `.symlinks/plugins/video_player_avfoundation/darwin`) - video_thumbnail (from `.symlinks/plugins/video_thumbnail/ios`) - volume_controller (from `.symlinks/plugins/volume_controller/ios`) @@ -336,6 +344,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/dart_ui_isolate/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" + emoji_picker_flutter: + :path: ".symlinks/plugins/emoji_picker_flutter/ios" ffmpeg_kit_flutter: :path: ".symlinks/plugins/ffmpeg_kit_flutter/ios" file_saver: @@ -428,6 +438,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/ua_client_hints/ios" url_launcher_ios: :path: ".symlinks/plugins/url_launcher_ios/ios" + vibration: + :path: ".symlinks/plugins/vibration/ios" video_player_avfoundation: :path: ".symlinks/plugins/video_player_avfoundation/darwin" video_thumbnail: @@ -446,6 +458,7 @@ SPEC CHECKSUMS: cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1 device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe + emoji_picker_flutter: ed468d9746c21711e66b2788880519a9de5de211 ffmpeg_kit_custom: 682b4f2f1ff1f8abae5a92f6c3540f2441d5be99 ffmpeg_kit_flutter: 915b345acc97d4142e8a9a8549d177ff10f043f5 file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6 @@ -497,6 +510,7 @@ SPEC CHECKSUMS: privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00 + rust_lib_photos: 04d3901908d2972192944083310b65abf410774c SDWebImage: f29024626962457f3470184232766516dee8dfea SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380 Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854 @@ -510,6 +524,7 @@ SPEC CHECKSUMS: thermal: d4c48be750d1ddbab36b0e2dcb2471531bc8df41 ua_client_hints: 92fe0d139619b73ec9fcb46cc7e079a26178f586 url_launcher_ios: 694010445543906933d732453a59da0a173ae33d + vibration: 8e2f50fc35bb736f9eecb7dd9f7047fbb6a6e888 video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b video_thumbnail: b637e0ad5f588ca9945f6e2c927f73a69a661140 volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12 diff --git a/mobile/apps/photos/ios/Runner.xcodeproj/project.pbxproj b/mobile/apps/photos/ios/Runner.xcodeproj/project.pbxproj index 56ba9d2b02..5471f4f021 100644 --- a/mobile/apps/photos/ios/Runner.xcodeproj/project.pbxproj +++ b/mobile/apps/photos/ios/Runner.xcodeproj/project.pbxproj @@ -532,6 +532,7 @@ "${BUILT_PRODUCTS_DIR}/cupertino_http/cupertino_http.framework", "${BUILT_PRODUCTS_DIR}/dart_ui_isolate/dart_ui_isolate.framework", "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/emoji_picker_flutter/emoji_picker_flutter.framework", "${BUILT_PRODUCTS_DIR}/file_saver/file_saver.framework", "${BUILT_PRODUCTS_DIR}/flutter_email_sender/flutter_email_sender.framework", "${BUILT_PRODUCTS_DIR}/flutter_image_compress_common/flutter_image_compress_common.framework", @@ -575,6 +576,7 @@ "${BUILT_PRODUCTS_DIR}/thermal/thermal.framework", "${BUILT_PRODUCTS_DIR}/ua_client_hints/ua_client_hints.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", + "${BUILT_PRODUCTS_DIR}/vibration/vibration.framework", "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", "${BUILT_PRODUCTS_DIR}/video_thumbnail/video_thumbnail.framework", "${BUILT_PRODUCTS_DIR}/volume_controller/volume_controller.framework", @@ -627,6 +629,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cupertino_http.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/dart_ui_isolate.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/emoji_picker_flutter.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_saver.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_email_sender.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_image_compress_common.framework", @@ -670,6 +673,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/thermal.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ua_client_hints.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/vibration.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_thumbnail.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/volume_controller.framework", From 9a876f3d59ac2ead34be014d60bf4e8449b202d6 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 19:11:10 +0530 Subject: [PATCH 155/164] fix: update internal and store change logs for widget tap issues on iOS --- mobile/apps/photos/scripts/internal_changes.txt | 1 + mobile/apps/photos/scripts/store_changes.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/mobile/apps/photos/scripts/internal_changes.txt b/mobile/apps/photos/scripts/internal_changes.txt index f6341c95b3..82485723c3 100644 --- a/mobile/apps/photos/scripts/internal_changes.txt +++ b/mobile/apps/photos/scripts/internal_changes.txt @@ -1,3 +1,4 @@ +- (prtk) Fix widget initial launch on iOS - (prtk) Upgrade Flutter version to 3.32.8 - (prtk) Run FFMpeg in an isolate - Neeraj: Handle custom domain links diff --git a/mobile/apps/photos/scripts/store_changes.txt b/mobile/apps/photos/scripts/store_changes.txt index 1d6ce14986..b230348287 100644 --- a/mobile/apps/photos/scripts/store_changes.txt +++ b/mobile/apps/photos/scripts/store_changes.txt @@ -1,3 +1,4 @@ +- Fix Widget tap on iOS - Added similar images debug screen (Settings > Backup > Free up space > Similar images) - Added support for custom domain links - Image editor fixes: From 5d3d18f347c221e9dd73c0aa197f8862e46d2791 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 19:59:15 +0530 Subject: [PATCH 156/164] fix: add some logs + init prefs at one place --- mobile/apps/photos/lib/app.dart | 1 + .../photos/lib/services/album_home_widget_service.dart | 7 +------ .../apps/photos/lib/services/home_widget_service.dart | 10 +++------- .../lib/services/memory_home_widget_service.dart | 7 +------ .../lib/services/people_home_widget_service.dart | 7 +------ 5 files changed, 7 insertions(+), 25 deletions(-) diff --git a/mobile/apps/photos/lib/app.dart b/mobile/apps/photos/lib/app.dart index 45bcb63019..5265134eae 100644 --- a/mobile/apps/photos/lib/app.dart +++ b/mobile/apps/photos/lib/app.dart @@ -90,6 +90,7 @@ class _EnteAppState extends State with WidgetsBindingObserver { } void _checkForWidgetLaunch() { + _logger.info("Checking for widget launch"); hw.HomeWidget.initiallyLaunchedFromHomeWidget().then( (uri) => HomeWidgetService.instance.onLaunchFromWidget(uri, context), ); diff --git a/mobile/apps/photos/lib/services/album_home_widget_service.dart b/mobile/apps/photos/lib/services/album_home_widget_service.dart index 9513427e1f..44abfef65a 100644 --- a/mobile/apps/photos/lib/services/album_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/album_home_widget_service.dart @@ -39,12 +39,7 @@ class AlbumHomeWidgetService { // Properties final Logger _logger = Logger((AlbumHomeWidgetService).toString()); - late final SharedPreferences _prefs; - - // Initialization - void init(SharedPreferences prefs) { - _prefs = prefs; - } + SharedPreferences get _prefs => HomeWidgetService.instance.prefs; // Public methods List? getSelectedAlbumIds() { diff --git a/mobile/apps/photos/lib/services/home_widget_service.dart b/mobile/apps/photos/lib/services/home_widget_service.dart index 3c86fbe51b..584249bc49 100644 --- a/mobile/apps/photos/lib/services/home_widget_service.dart +++ b/mobile/apps/photos/lib/services/home_widget_service.dart @@ -55,16 +55,12 @@ class HomeWidgetService { final Logger _logger = Logger((HomeWidgetService).toString()); final computeLock = Lock(); + late final SharedPreferences prefs; void init(SharedPreferences prefs) { + _logger.info("Initializing HomeWidgetService"); setAppGroupID(iOSGroupIDMemory); - _initializeWidgetServices(prefs); - } - - void _initializeWidgetServices(SharedPreferences prefs) { - AlbumHomeWidgetService.instance.init(prefs); - PeopleHomeWidgetService.instance.init(prefs); - MemoryHomeWidgetService.instance.init(prefs); + this.prefs = prefs; } void setAppGroupID(String id) { diff --git a/mobile/apps/photos/lib/services/memory_home_widget_service.dart b/mobile/apps/photos/lib/services/memory_home_widget_service.dart index ca69b75eb4..2539ba123a 100644 --- a/mobile/apps/photos/lib/services/memory_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/memory_home_widget_service.dart @@ -31,12 +31,7 @@ class MemoryHomeWidgetService { // Properties final Logger _logger = Logger((MemoryHomeWidgetService).toString()); - late final SharedPreferences _prefs; - - // Initialization - void init(SharedPreferences prefs) { - _prefs = prefs; - } + SharedPreferences get _prefs => HomeWidgetService.instance.prefs; // Preference getters and setters bool? hasLastYearMemoriesSelected() { diff --git a/mobile/apps/photos/lib/services/people_home_widget_service.dart b/mobile/apps/photos/lib/services/people_home_widget_service.dart index c204e140b1..fea8366ecf 100644 --- a/mobile/apps/photos/lib/services/people_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/people_home_widget_service.dart @@ -35,14 +35,9 @@ class PeopleHomeWidgetService { // Properties final Logger _logger = Logger((PeopleHomeWidgetService).toString()); - late final SharedPreferences _prefs; + SharedPreferences get _prefs => HomeWidgetService.instance.prefs; final peopleChangedLock = Lock(); - // Initialization - void init(SharedPreferences prefs) { - _prefs = prefs; - } - // Public methods List? getSelectedPeople() { return _prefs.getStringList(SELECTED_PEOPLE_KEY); From 3e2dbe2c1b3b81bdb679708398720ddda6837d9a Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 20:47:01 +0530 Subject: [PATCH 157/164] fix: take prefs from ServiceLocation & setAppGroup whenever needed --- mobile/apps/photos/lib/app.dart | 6 +++--- mobile/apps/photos/lib/main.dart | 3 --- .../services/album_home_widget_service.dart | 2 +- .../lib/services/home_widget_service.dart | 21 ++++++++++++------- .../services/memory_home_widget_service.dart | 2 +- .../services/people_home_widget_service.dart | 2 +- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/mobile/apps/photos/lib/app.dart b/mobile/apps/photos/lib/app.dart index 5265134eae..a872a704df 100644 --- a/mobile/apps/photos/lib/app.dart +++ b/mobile/apps/photos/lib/app.dart @@ -89,9 +89,9 @@ class _EnteAppState extends State with WidgetsBindingObserver { _checkForWidgetLaunch(); } - void _checkForWidgetLaunch() { - _logger.info("Checking for widget launch"); - hw.HomeWidget.initiallyLaunchedFromHomeWidget().then( + Future _checkForWidgetLaunch() async { + await HomeWidgetService.instance.setAppGroup(); + await hw.HomeWidget.initiallyLaunchedFromHomeWidget().then( (uri) => HomeWidgetService.instance.onLaunchFromWidget(uri, context), ); hw.HomeWidget.widgetClicked.listen( diff --git a/mobile/apps/photos/lib/main.dart b/mobile/apps/photos/lib/main.dart index 152f1d98e1..585170bc32 100644 --- a/mobile/apps/photos/lib/main.dart +++ b/mobile/apps/photos/lib/main.dart @@ -174,7 +174,6 @@ Future _runMinimally(String taskId, TimeLogger tlog) async { // Misc Services await UserService.instance.init(); NotificationService.instance.init(prefs); - if (Platform.isAndroid) HomeWidgetService.instance.init(prefs); // Begin Execution // only runs for android @@ -224,8 +223,6 @@ Future _init(bool isBackground, {String via = ''}) async { Computer.shared().turnOn(workersCount: 4).ignore(); CryptoUtil.init(); - HomeWidgetService.instance.init(preferences); - _logger.info("Lockscreen init $tlog"); unawaited(LockScreenSettings.instance.init(preferences)); diff --git a/mobile/apps/photos/lib/services/album_home_widget_service.dart b/mobile/apps/photos/lib/services/album_home_widget_service.dart index 44abfef65a..7cf07d9e13 100644 --- a/mobile/apps/photos/lib/services/album_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/album_home_widget_service.dart @@ -39,7 +39,7 @@ class AlbumHomeWidgetService { // Properties final Logger _logger = Logger((AlbumHomeWidgetService).toString()); - SharedPreferences get _prefs => HomeWidgetService.instance.prefs; + SharedPreferences get _prefs => ServiceLocator.instance.prefs; // Public methods List? getSelectedAlbumIds() { diff --git a/mobile/apps/photos/lib/services/home_widget_service.dart b/mobile/apps/photos/lib/services/home_widget_service.dart index 584249bc49..c0b41da862 100644 --- a/mobile/apps/photos/lib/services/home_widget_service.dart +++ b/mobile/apps/photos/lib/services/home_widget_service.dart @@ -55,19 +55,24 @@ class HomeWidgetService { final Logger _logger = Logger((HomeWidgetService).toString()); final computeLock = Lock(); - late final SharedPreferences prefs; + SharedPreferences? prefs; - void init(SharedPreferences prefs) { - _logger.info("Initializing HomeWidgetService"); - setAppGroupID(iOSGroupIDMemory); - this.prefs = prefs; + bool _isAppGroupSet = false; + + Future setAppGroup() async { + if (_isAppGroupSet) return; + _logger.info("Setting app group id"); + await setAppGroupID(iOSGroupIDMemory); + _isAppGroupSet = true; } - void setAppGroupID(String id) { - hw.HomeWidget.setAppGroupId(id).ignore(); + Future setAppGroupID(String id) async { + if (!Platform.isIOS) return; + await hw.HomeWidget.setAppGroupId(id); } Future initHomeWidget([bool isBg = false]) async { + await setAppGroup(); await AlbumHomeWidgetService.instance.initAlbumHomeWidget(isBg); await PeopleHomeWidgetService.instance.initPeopleHomeWidget(); await MemoryHomeWidgetService.instance.initMemoryHomeWidget(); @@ -214,7 +219,7 @@ class HomeWidgetService { Future clearWidget(bool autoLogout) async { if (autoLogout) { - setAppGroupID(iOSGroupIDMemory); + await setAppGroup(); } await Future.wait([ diff --git a/mobile/apps/photos/lib/services/memory_home_widget_service.dart b/mobile/apps/photos/lib/services/memory_home_widget_service.dart index 2539ba123a..a78a650a9d 100644 --- a/mobile/apps/photos/lib/services/memory_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/memory_home_widget_service.dart @@ -31,7 +31,7 @@ class MemoryHomeWidgetService { // Properties final Logger _logger = Logger((MemoryHomeWidgetService).toString()); - SharedPreferences get _prefs => HomeWidgetService.instance.prefs; + SharedPreferences get _prefs => ServiceLocator.instance.prefs; // Preference getters and setters bool? hasLastYearMemoriesSelected() { diff --git a/mobile/apps/photos/lib/services/people_home_widget_service.dart b/mobile/apps/photos/lib/services/people_home_widget_service.dart index fea8366ecf..323bac5351 100644 --- a/mobile/apps/photos/lib/services/people_home_widget_service.dart +++ b/mobile/apps/photos/lib/services/people_home_widget_service.dart @@ -35,7 +35,7 @@ class PeopleHomeWidgetService { // Properties final Logger _logger = Logger((PeopleHomeWidgetService).toString()); - SharedPreferences get _prefs => HomeWidgetService.instance.prefs; + SharedPreferences get _prefs => ServiceLocator.instance.prefs; final peopleChangedLock = Lock(); // Public methods From 5541198967a8659bf96a9c8a2dd66b95a8e78e3a Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 20:55:07 +0530 Subject: [PATCH 158/164] fix: combine functions --- .../photos/lib/services/home_widget_service.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mobile/apps/photos/lib/services/home_widget_service.dart b/mobile/apps/photos/lib/services/home_widget_service.dart index c0b41da862..b5203cecc4 100644 --- a/mobile/apps/photos/lib/services/home_widget_service.dart +++ b/mobile/apps/photos/lib/services/home_widget_service.dart @@ -59,18 +59,18 @@ class HomeWidgetService { bool _isAppGroupSet = false; - Future setAppGroup() async { - if (_isAppGroupSet) return; + Future setAppGroup({String id = iOSGroupIDMemory}) async { + if (!Platform.isIOS || _isAppGroupSet) return; _logger.info("Setting app group id"); - await setAppGroupID(iOSGroupIDMemory); + await hw.HomeWidget.setAppGroupId(id).catchError( + (error) { + _logger.severe("Failed to set app group ID: $error"); + return null; + }, + ); _isAppGroupSet = true; } - Future setAppGroupID(String id) async { - if (!Platform.isIOS) return; - await hw.HomeWidget.setAppGroupId(id); - } - Future initHomeWidget([bool isBg = false]) async { await setAppGroup(); await AlbumHomeWidgetService.instance.initAlbumHomeWidget(isBg); From 354bcc715f1ab034d65a7443381c0291cacba1f3 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 20:56:46 +0530 Subject: [PATCH 159/164] fix: remove iOS widget tap issue from change log --- mobile/apps/photos/scripts/store_changes.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/mobile/apps/photos/scripts/store_changes.txt b/mobile/apps/photos/scripts/store_changes.txt index b230348287..1d6ce14986 100644 --- a/mobile/apps/photos/scripts/store_changes.txt +++ b/mobile/apps/photos/scripts/store_changes.txt @@ -1,4 +1,3 @@ -- Fix Widget tap on iOS - Added similar images debug screen (Settings > Backup > Free up space > Similar images) - Added support for custom domain links - Image editor fixes: From db88432b9dddddb9101f768cdc78429af9635f45 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 21:54:00 +0530 Subject: [PATCH 160/164] fix: update swift file to handle homeWidget deep link --- mobile/apps/photos/ios/Runner/AppDelegate.swift | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/ios/Runner/AppDelegate.swift b/mobile/apps/photos/ios/Runner/AppDelegate.swift index cbf287bc52..ea777b81ad 100644 --- a/mobile/apps/photos/ios/Runner/AppDelegate.swift +++ b/mobile/apps/photos/ios/Runner/AppDelegate.swift @@ -27,9 +27,12 @@ import workmanager // Retrieve the link from parameters if let url = AppLinks.shared.getLink(launchOptions: launchOptions) { - // We have a link, propagate it to your Flutter app or not - AppLinks.shared.handleLink(url: url) - return true // Returning true will stop the propagation to other packages + // only accept non-homewidget urls for AppLinks + if let host = url.host, !host.contains("&homeWidget") { + AppLinks.shared.handleLink(url: url) + // We have a link, propagate it to your Flutter app or not + return true // Returning true will stop the propagation to other packages + } } return super.application(application, didFinishLaunchingWithOptions: launchOptions) From 1ce749e93e9e1585525d67bb95b435bf009ed9aa Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 22:22:54 +0530 Subject: [PATCH 161/164] fix: update code to check correctly --- mobile/apps/photos/ios/Runner/AppDelegate.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mobile/apps/photos/ios/Runner/AppDelegate.swift b/mobile/apps/photos/ios/Runner/AppDelegate.swift index ea777b81ad..8673b08bc4 100644 --- a/mobile/apps/photos/ios/Runner/AppDelegate.swift +++ b/mobile/apps/photos/ios/Runner/AppDelegate.swift @@ -28,10 +28,10 @@ import workmanager // Retrieve the link from parameters if let url = AppLinks.shared.getLink(launchOptions: launchOptions) { // only accept non-homewidget urls for AppLinks - if let host = url.host, !host.contains("&homeWidget") { + if !url.absoluteString.contains("&homeWidget") { AppLinks.shared.handleLink(url: url) - // We have a link, propagate it to your Flutter app or not - return true // Returning true will stop the propagation to other packages + // link is handled, stop propagation + return true } } From 9a654988f861bd4f767be6ba7a9675617878f1f7 Mon Sep 17 00:00:00 2001 From: Prateek Sunal Date: Mon, 18 Aug 2025 22:23:15 +0530 Subject: [PATCH 162/164] chore: remove unused prefs --- mobile/apps/photos/lib/services/home_widget_service.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/mobile/apps/photos/lib/services/home_widget_service.dart b/mobile/apps/photos/lib/services/home_widget_service.dart index b5203cecc4..f6f9e057b2 100644 --- a/mobile/apps/photos/lib/services/home_widget_service.dart +++ b/mobile/apps/photos/lib/services/home_widget_service.dart @@ -55,8 +55,6 @@ class HomeWidgetService { final Logger _logger = Logger((HomeWidgetService).toString()); final computeLock = Lock(); - SharedPreferences? prefs; - bool _isAppGroupSet = false; Future setAppGroup({String id = iOSGroupIDMemory}) async { From fcf038c4d8a415b48269406662aee8eda23d0434 Mon Sep 17 00:00:00 2001 From: Matthias Wirtz Date: Mon, 18 Aug 2025 20:54:12 +0200 Subject: [PATCH 163/164] pin to specific release of debian to avoid collation mismatch --- server/config/compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/config/compose.yaml b/server/config/compose.yaml index aa2189c545..6cb55c86cb 100644 --- a/server/config/compose.yaml +++ b/server/config/compose.yaml @@ -21,7 +21,7 @@ services: command: "TCP-LISTEN:3200,fork,reuseaddr TCP:minio:3200" postgres: - image: postgres:15 + image: postgres:15-trixie env_file: - .env # Wait for postgres to accept connections before starting museum. From 9771a5bc5d3ba88a47b34fd4ab9c8ae0e7151b99 Mon Sep 17 00:00:00 2001 From: vishnukvmd Date: Tue, 19 Aug 2025 11:41:44 +0530 Subject: [PATCH 164/164] Remove ignore --- mobile/apps/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 100644 mobile/apps/.gitignore diff --git a/mobile/apps/.gitignore b/mobile/apps/.gitignore deleted file mode 100644 index 201fd57dc6..0000000000 --- a/mobile/apps/.gitignore +++ /dev/null @@ -1 +0,0 @@ -surprise/

    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%0DM-kPd{OSFP&ge_j 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 ZGqcNg3-{l5#UH@Px9&`BPe(XS_-~kqMzH_@ literal 0 HcmV?d00001 diff --git a/mobile/apps/locker/assets/fonts/Inter-SemiBold.ttf b/mobile/apps/locker/assets/fonts/Inter-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..4be54399d679a0cc0ed46526f79d8e53cec2a1a5 GIT binary patch literal 710040 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 z%0A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