From 11684a3cbe55bf09b06d4d12928431d68b25d53b Mon Sep 17 00:00:00 2001 From: "Mike V." <113628339+mikev-cw@users.noreply.github.com> Date: Wed, 28 Jan 2026 09:45:59 +0100 Subject: [PATCH 01/20] Added code generation step on release publish action --- .github/workflows/publish-release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e03b8361..ec3f0028 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -30,6 +30,10 @@ jobs: - name: Install dependencies run: flutter pub get + + - name: Run build_runner + run: | + dart run build_runner build --delete-conflicting-outputs - name: Dump keystore uses: timheuer/base64-to-file@v1.2.4 From a4b2e1620fb7db7a4a3de6f8a9987f0e9b2ad092 Mon Sep 17 00:00:00 2001 From: Alessandro Bonomo <75626033+AlessandroBonomo28@users.noreply.github.com> Date: Tue, 3 Feb 2026 04:09:07 +0100 Subject: [PATCH 02/20] Dettaglio export popup avviso su android MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit quando l'utente prova a salvare nella root directory di android ha un output più chiaro sul perchè non funziona e su dove salvare il file --- lib/services/csv/csv_file_picker.dart | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/services/csv/csv_file_picker.dart b/lib/services/csv/csv_file_picker.dart index fdae37e8..bea66b89 100644 --- a/lib/services/csv/csv_file_picker.dart +++ b/lib/services/csv/csv_file_picker.dart @@ -53,9 +53,10 @@ class CSVFilePicker { // Share exported CSV file static Future saveCSVFile(String csv, BuildContext context) async { + String? selectedDirectory; try { // Prompt the user to select a directory - String? selectedDirectory = await FilePicker.platform.getDirectoryPath(); + selectedDirectory = await FilePicker.platform.getDirectoryPath(); if (selectedDirectory == null) { // User canceled the picker return; @@ -76,7 +77,17 @@ class CSVFilePicker { } } catch (e) { if (context.mounted) { - showSnackBar(context, message: 'Error saving file: ${e.toString()}'); + String errorMessage = 'Error saving file: ${e.toString()}'; + + // Check if error is due to saving in root directory on Android + if (Platform.isAndroid && + selectedDirectory != null && + (selectedDirectory == '/storage/emulated/0' || + selectedDirectory == '/storage/emulated/0/')) { + errorMessage = 'Cannot save to device root. Please create or select a folder in Downloads or Documents.'; + } + + showSnackBar(context, message: errorMessage); } } } From 3e085db135a2726ccf3863f8ab4fa75e60492236 Mon Sep 17 00:00:00 2001 From: Alessandro Bonomo <75626033+AlessandroBonomo28@users.noreply.github.com> Date: Thu, 5 Feb 2026 21:50:55 +0100 Subject: [PATCH 03/20] simplified error message --- lib/services/csv/csv_file_picker.dart | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/services/csv/csv_file_picker.dart b/lib/services/csv/csv_file_picker.dart index bea66b89..e177c155 100644 --- a/lib/services/csv/csv_file_picker.dart +++ b/lib/services/csv/csv_file_picker.dart @@ -77,15 +77,8 @@ class CSVFilePicker { } } catch (e) { if (context.mounted) { - String errorMessage = 'Error saving file: ${e.toString()}'; + String errorMessage = 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: ${e.toString()}'; - // Check if error is due to saving in root directory on Android - if (Platform.isAndroid && - selectedDirectory != null && - (selectedDirectory == '/storage/emulated/0' || - selectedDirectory == '/storage/emulated/0/')) { - errorMessage = 'Cannot save to device root. Please create or select a folder in Downloads or Documents.'; - } showSnackBar(context, message: errorMessage); } From 36c439e83f2fbc939fd6684bef4bdf27e38bd273 Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Fri, 6 Feb 2026 21:47:02 +0100 Subject: [PATCH 04/20] Formatted code --- lib/services/csv/csv_file_picker.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/services/csv/csv_file_picker.dart b/lib/services/csv/csv_file_picker.dart index e177c155..eef1444b 100644 --- a/lib/services/csv/csv_file_picker.dart +++ b/lib/services/csv/csv_file_picker.dart @@ -77,9 +77,9 @@ class CSVFilePicker { } } catch (e) { if (context.mounted) { - String errorMessage = 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: ${e.toString()}'; - - + String errorMessage = + 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: ${e.toString()}'; + showSnackBar(context, message: errorMessage); } } From 93ec7e908b42e54dc59807ca193fa86cd480cf57 Mon Sep 17 00:00:00 2001 From: Luca Antonelli <45290704+lucaantonelli@users.noreply.github.com> Date: Fri, 20 Feb 2026 14:25:12 +0100 Subject: [PATCH 05/20] build: pubspec update + ios settings update (#509) * build: update project and ios settings * fix: visual bug on transactions header * ci: fix flutter version update * fix: subcategories not counting in budgets --- .fvmrc | 2 +- .github/workflows/ci-cd.yml | 7 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile | 6 +- ios/Podfile.lock | 43 +++-- ios/Runner.xcodeproj/project.pbxproj | 98 +++++------ .../xcshareddata/xcschemes/Runner.xcscheme | 2 + .../AppIcon.appiconset/Contents.json | 118 ++++++------- .../AppIcon.appiconset/Icon-App-50x50@1x.png | Bin 1732 -> 0 bytes .../AppIcon.appiconset/Icon-App-50x50@2x.png | Bin 3821 -> 0 bytes .../AppIcon.appiconset/Icon-App-57x57@1x.png | Bin 2040 -> 0 bytes .../AppIcon.appiconset/Icon-App-57x57@2x.png | Bin 4303 -> 0 bytes .../AppIcon.appiconset/Icon-App-72x72@1x.png | Bin 2717 -> 0 bytes .../AppIcon.appiconset/Icon-App-72x72@2x.png | Bin 5611 -> 0 bytes lib/model/transaction.dart | 4 + lib/pages/planning/widget/budget_card.dart | 7 +- lib/pages/transactions/transactions_page.dart | 105 +++++------ lib/providers/categories_provider.dart | 6 +- lib/providers/transactions_provider.dart | 1 + .../repositories/transactions_repository.dart | 2 +- .../notifications/notifications_service.dart | 14 +- pubspec.lock | 166 +++++------------- pubspec.yaml | 20 +-- 23 files changed, 284 insertions(+), 319 deletions(-) delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png delete mode 100644 ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png diff --git a/.fvmrc b/.fvmrc index a06c1dbd..5a92dd32 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,3 +1,3 @@ { - "flutter": "3.38.3" + "flutter": "3.38.8" } \ No newline at end of file diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index 08ac1451..5751b813 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -34,9 +34,14 @@ jobs: flutter-version: ${{ steps.fvm-config-action.outputs.FLUTTER_VERSION }} channel: stable cache: true - cache-key: flutter + cache-key: flutter-${{ steps.fvm-config-action.outputs.FLUTTER_VERSION }} cache-path: ${{ runner.tool_cache }}/flutter + - name: Check versions + run: | + flutter --version + dart --version + - name: Get dependencies run: flutter pub get diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 7c569640..1dc6cf76 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 12.0 + 13.0 diff --git a/ios/Podfile b/ios/Podfile index e549ee22..c38adb31 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -39,5 +39,9 @@ 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' + end end end diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 7e4012a7..4070ec8e 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -40,30 +40,35 @@ PODS: - Flutter - flutter_native_splash (2.4.3): - Flutter + - local_auth_darwin (0.0.1): + - Flutter + - FlutterMacOS + - package_info_plus (0.4.5): + - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - - SDWebImage (5.21.0): - - SDWebImage/Core (= 5.21.0) - - SDWebImage/Core (5.21.0) + - SDWebImage (5.21.5): + - SDWebImage/Core (= 5.21.5) + - SDWebImage/Core (5.21.5) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - sqflite_darwin (0.0.4): - Flutter - FlutterMacOS - - sqlite3 (3.49.1): - - sqlite3/common (= 3.49.1) - - sqlite3/common (3.49.1) - - sqlite3/dbstatvtab (3.49.1): + - sqlite3 (3.49.2): + - sqlite3/common (= 3.49.2) + - sqlite3/common (3.49.2) + - sqlite3/dbstatvtab (3.49.2): - sqlite3/common - - sqlite3/fts5 (3.49.1): + - sqlite3/fts5 (3.49.2): - sqlite3/common - - sqlite3/perf-threadsafe (3.49.1): + - sqlite3/perf-threadsafe (3.49.2): - sqlite3/common - - sqlite3/rtree (3.49.1): + - sqlite3/rtree (3.49.2): - sqlite3/common - sqlite3_flutter_libs (0.0.1): - Flutter @@ -83,6 +88,8 @@ DEPENDENCIES: - Flutter (from `Flutter`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) + - local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) @@ -109,6 +116,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/flutter_local_notifications/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" + local_auth_darwin: + :path: ".symlinks/plugins/local_auth_darwin/darwin" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: @@ -127,19 +138,21 @@ SPEC CHECKSUMS: DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: b159e0c068aef54932bb15dc9fd1571818edaf49 - Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 + local_auth_darwin: 63c73d6d28cc3e239be2b6aa460ea6e317cd5100 + package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868 + SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838 shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d - sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983 + sqlite3: 3c950dc86011117c307eb0b28c4a7bb449dce9f1 sqlite3_flutter_libs: cc304edcb8e1d8c595d1b08c7aeb46a47691d9db SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe -PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5 +PODFILE CHECKSUM: 9825c73c49f0e8b8218ad917d16459c48629562e COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 41880065..76f392c8 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 60717B488D117C302D51AD39 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C46632FB1D4031F1C40B27AD /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 7B79FD3812491357C7C5FC49 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4B46397FFB47DD9AC71FA59C /* 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 */; }; - 9EEE9CFD1687A2D041886DAC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C2757B8039543ECD685C8D2 /* Pods_Runner.framework */; }; + B6B73809C111246E5879609E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BEA362A8E4D1D74E4515E2A /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,19 +42,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 12C7AD89E8E781A4BFC539F1 /* 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 = ""; }; 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; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 4B46397FFB47DD9AC71FA59C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 4FCD4FD40BD97E34D03FCDA6 /* 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 = ""; }; - 5CF8043730145DC9CD3F4BF0 /* 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 = ""; }; + 475C63D1361FE5A5162726B7 /* 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 = ""; }; + 5BEA362A8E4D1D74E4515E2A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.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 = ""; }; - 7C2757B8039543ECD685C8D2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 89F66B4ACA61A6ED13CC09E2 /* 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 = ""; }; + 7E765B070269A0B32DDAEC06 /* 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 = ""; }; 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; }; @@ -62,9 +61,10 @@ 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 = ""; }; - B64959616C10CCE4F34B5510 /* 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 = ""; }; - C4D8942E64375523F16CE99E /* 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 = ""; }; - CDC781DEDE80B5D260EC7F4F /* 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 = ""; }; + C03D3953861AE792AFE87028 /* 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 = ""; }; + C46632FB1D4031F1C40B27AD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C5FD826CD5C9D80E8465B6AE /* 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 = ""; }; + E18EF08C0830BCDB09043362 /* 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 */ @@ -72,7 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 7B79FD3812491357C7C5FC49 /* Pods_RunnerTests.framework in Frameworks */, + B6B73809C111246E5879609E /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,7 +80,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9EEE9CFD1687A2D041886DAC /* Pods_Runner.framework in Frameworks */, + 60717B488D117C302D51AD39 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -90,12 +90,12 @@ 2B7BE9956F74F52F280FD060 /* Pods */ = { isa = PBXGroup; children = ( - 4FCD4FD40BD97E34D03FCDA6 /* Pods-Runner.debug.xcconfig */, - 5CF8043730145DC9CD3F4BF0 /* Pods-Runner.release.xcconfig */, - 89F66B4ACA61A6ED13CC09E2 /* Pods-Runner.profile.xcconfig */, - B64959616C10CCE4F34B5510 /* Pods-RunnerTests.debug.xcconfig */, - CDC781DEDE80B5D260EC7F4F /* Pods-RunnerTests.release.xcconfig */, - C4D8942E64375523F16CE99E /* Pods-RunnerTests.profile.xcconfig */, + C5FD826CD5C9D80E8465B6AE /* Pods-Runner.debug.xcconfig */, + E18EF08C0830BCDB09043362 /* Pods-Runner.release.xcconfig */, + 475C63D1361FE5A5162726B7 /* Pods-Runner.profile.xcconfig */, + C03D3953861AE792AFE87028 /* Pods-RunnerTests.debug.xcconfig */, + 7E765B070269A0B32DDAEC06 /* Pods-RunnerTests.release.xcconfig */, + 12C7AD89E8E781A4BFC539F1 /* Pods-RunnerTests.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -108,11 +108,11 @@ path = RunnerTests; sourceTree = ""; }; - 944B175A5F520A814EAB6758 /* Frameworks */ = { + 8110465CD8424934EFBDF0FA /* Frameworks */ = { isa = PBXGroup; children = ( - 7C2757B8039543ECD685C8D2 /* Pods_Runner.framework */, - 4B46397FFB47DD9AC71FA59C /* Pods_RunnerTests.framework */, + C46632FB1D4031F1C40B27AD /* Pods_Runner.framework */, + 5BEA362A8E4D1D74E4515E2A /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -136,7 +136,7 @@ 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, 2B7BE9956F74F52F280FD060 /* Pods */, - 944B175A5F520A814EAB6758 /* Frameworks */, + 8110465CD8424934EFBDF0FA /* Frameworks */, ); sourceTree = ""; }; @@ -171,7 +171,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - BC6D36AA21B1C35DE23D3B33 /* [CP] Check Pods Manifest.lock */, + ADF665451747D4BB42C12B05 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, 09967A90A294A1D6A4428C6B /* Frameworks */, @@ -190,15 +190,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - F6FA79FDA92D20292C44C4B1 /* [CP] Check Pods Manifest.lock */, + 5CF2A4C07BB70DFF72346B2D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 211392328957DBA96447070D /* [CP] Embed Pods Frameworks */, - 6090A52EFEF53161A56AE5DE /* [CP] Copy Pods Resources */, + 279A6739CEE2A8E4D77C3063 /* [CP] Embed Pods Frameworks */, + F6CDA2B012B301A90701A2B4 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -270,7 +270,7 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 211392328957DBA96447070D /* [CP] Embed Pods Frameworks */ = { + 279A6739CEE2A8E4D77C3063 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -303,21 +303,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 6090A52EFEF53161A56AE5DE /* [CP] Copy Pods Resources */ = { + 5CF2A4C07BB70DFF72346B2D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Copy Pods Resources"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; + 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; }; 9740EEB61CF901F6004384FC /* Run Script */ = { @@ -335,7 +340,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - BC6D36AA21B1C35DE23D3B33 /* [CP] Check Pods Manifest.lock */ = { + ADF665451747D4BB42C12B05 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -357,26 +362,21 @@ 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; }; - F6FA79FDA92D20292C44C4B1 /* [CP] Check Pods Manifest.lock */ = { + F6CDA2B012B301A90701A2B4 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Copy Pods Resources"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist", ); 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"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -470,7 +470,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -506,7 +506,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B64959616C10CCE4F34B5510 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = C03D3953861AE792AFE87028 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -524,7 +524,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = CDC781DEDE80B5D260EC7F4F /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 7E765B070269A0B32DDAEC06 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -540,7 +540,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = C4D8942E64375523F16CE99E /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 12C7AD89E8E781A4BFC539F1 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -601,7 +601,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -650,7 +650,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 15cada48..e3773d42 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> }pKSj*$mUv!++B9D;mq9Gdw1`D@BjVhobP<+U&$g2 z{{xd;Jpo5q9gx~)bwFyH)d-OCatqwtAPzUaCSoLRr0e@%IoxUl$o5_Pu;GiH_#pFb z`1>}5z=h|pD#iBw8iN7OeBH> z{T%*IhXJARF^i=)n#$p(P#`KI1l|q3a93xvdcb5h<5YAOK<2C!xN-AV#X}_tty_j7EVL=So~{Q!UvHbSCX)$!_8#VA;+PZ+9h?Y* zSPYFy>3kscVgix3^#@Fyv6O+S)hY~2O+;cs3=SSG!nNzA7?zgwkCk3Fe+o8>ak*da z#KL78&^taFej;z24uo=n;``LE=aD%(he7ywdm(4xdx!`R#u=}{k^ge zvl6k@Q(6@hgrdGDCXY|!g(0&i-DLrxN!%#Cg@S!Y`MPtbHt5(s3TjF5Sm8mTMqmjP zd6vRdQaYKnD(l6GQ^lz4$+|t?mOs@}^Mp>)nQS0hjmCO@^Y`1dysWA0j1bvYZ6Fj5l)oXtO*pjZ`n&x7TnqF@6WrCV=?rAO zRVk7*v{?Wj#pf^Mkf<3{7m|8*7uw3iIpl1U1%%j%D0tG?RE$oa%V9Nr)^e0wzK%Ci zdW(Lf+|l!_(&TwWQK4L;yFH7H2}3Yv!CIIFB`^JC6Y_sNgdwl>`p>aOaUV$=8n0Bi zIjlF4pX@@hQXxy}pMX}8&AB)pJaQ5SgYl8$h_T6=Ece|i7>hjYGWAO@CdEnxt$o`F ztjn2!<0sGJ@GqyT9Gji95iP=k5F6czff-C@einIJuGvK~l?6>DS%g$EBPueK57JtX zvVwg_ux!;P)?y@4;$og-lJF!jN_k)$!j%;3?qU*JvAib1Sc)oEwu5MQ3s0QEB|#2j zCx6HUkuHd%OymyfFgao7P|{}&B`Tt_iuQHVxKt$ejKTPfdAM-#Dpr5C1>vDVNb1#H zc3P6yE5mvG;02}ptPj6x`_30gp#(q9% z9TkS34isYcf;GGgL%(HZyv2K9OwG=$?9zQ8BZv0I&j*j=n5fcAR_3yX>e#L&2KDP< zy|?t{ZS44VFWg1g94$PJHS4!c# zKJvEZW9!#@aNzJs{CTI$=H81XS21h;YU?#IUh|MZ_RzrOm*C-{<=R4`(Vh(m*-Jnn z;Hi~3!6~fzz7hwWPRE7qOz|b_eg3{a7&CkT>eur`6Hy}~TQuVkt281Bm&$lDlHH!# zisU9LmXz*@2n)uUb0yY${QbN!>dk)06uI=3@Y?v1gAgbH5oN0!0HD$wrhb(QC9f`p zPV0zf)~z9mdpf@$Jay#QX;{k50=&u*ueC9UOB7n+s20J@M0wkHa|ja9d0AtSK0O;M zRn4fP8hI*#8S-j8(gwz(QqhM5HAe30nLrI~u+*4nBrp$e)-fwRN&o7)!s>w3Hmd_t a+w3n+)N$K;6)A}T0000t1QCKOAt<^WA|QNRt01lji;Jwd2nq-Y zB69BnvK$))BnTqNO$c`gx#zO}n(0Z;^yDI>yJP16zG3K|n(of~tKNI{-m4nDoq>E% z4fIMsfDi8Q5u!5q2vHe)gs2QYLR1DHAu5B95S774h|1t2L}l<1qB8ghQ5k%Ms0^is z5S!gj7ORbP8VzZ+8q%OxSb&{srBO~<9>T>c*6G{ysz_|-oXxE+u+O%~q&6+%l2KJ4TkaFVJ zvvmIARobw5H!b;a5_O6UTo z*d(8wzjT$d&DMN{eg0w+rDs`4r_+$0sW-jOyF;KBsFD!7lY|^b0t~f)nl-9Y)Z^jQ zxM2_l)u~C18Uz)1Et}QONwrz*WF`lxI%en%dMz1rTGf%GDndX6NXW5R$y=s>@?VET zGZ92+Wh(1#Y8qX>5IsZjUf?s1NlvdBtCMvT#*Q;p$Q1tAbVMnsm` z;*|)C#X^UVoub1>Ptx&|XX*6s7wPJ?o9w)me~w=MX51he{?apfUjJ(H4XPbjT{0rj z0nF@G&ZKAROzZXgazb%JFe&BSRRbDPtp zQ#keP-jN<{+tT%Y^nr|Q3zhfNOWbI=p(r6B1A^doLL}Q`znrG|i&oPAfBMBGv|{Ue z_WfJ8@6uXE@Yfr5Qb^-qdVB0JihMN8)dbHH;Ac?Ot|&$bX5Jau%*={Z6a@Cc!WH!G zmc5*;f&?R!lxelvB85qC-7{w|)A0Bi6c^i%Mh)-hydLq<3iYKF6uGl7GQ?#3c;p0) zoiv|r-$~8$I|zw%h-giZv}s16jq6kG+SRE_<%(o77N%EwuLoSXWDT1*R-d-k9TOxkB=}SYzFlkn+IQe6&0VmZ&YZhUi6_pu2(cC7Iwb{J z3Bkw^tE$-_EThXw*Cp+G#dM-MQ%5sF7wm#!?toc0kmDu10-(2pEQ{6s^T*mhM3L>n zXw|xHbm{Un*Ll11rGyocjS%pVW6l*wB6Dpwe!oZ3u6bw!n)%LaZe(CKU7Sf9LL?@G z#%m-%&ur4*{yp4OriPRugFla4H{$Um(dhX6%kLy@piX8^9z{mxH3{%D>bP3+?A`YR zTpyza1cMj^CIuMuxuVc0nADPu5V1hvA?@0mAZgR}iFVYCjSvA2;XQccDiDKFmz86Y z*dBdZAOyK?0Y4r+L1weNR|R$QeD7%2F@k+YeaVI~5cTzElR%6VvHdZW@O|@xd@4)S z3s0UpC;8l9&{Mncmab!?L7vb!?%`)d49iNH%mV~TO2)HfA;iw@NA7V_@=ZybdO?AV zgk0Mpw@?gBHG&+a*Z0Xoh{tj2y>v;Ns#PlH#;@4B5^B7Rh2L{Mdz)8}NyLCkl0`~D z7D6H_;{6~LIFM{A|K+Q` z<5eZx+p#+`ct{qhr2s(=$VLe2p@c|9i>8ezux2&7cH_2Nn}owBXyfKR)Th@I{1`Ln z^h`6EjOwWhDQU<^2wX}BBgn?}@7s+&Ub5P)4MxU{xl5@@<6vslyfHtAF(nS!|JflM zArLl55n@^|4d_8%t=q<{*~Q4p&Y{>bGilz;H>hJo8@V1&HIOwQf`FbtNcr;RcmnFx zH)iuXT`@3m9QpbzdNMkaVu$vn=AjMcd`v|lDy@2dFb;l@iUFAGWu-_N5PB)f zHZjk@d;{`t(kPg|Tsn*Xvp0d3F5k$A`K`ozup^yobEbIJL5yN0dI+zhY%`OQVsY9J z5D?wD9e*7^`8#d+ZZ~cJ@4>uk+=>L0{#33MK+QrLNP?GUXK9j<-Yt`*q=2BlP-vZH zcK5U}55a5FTf^wO#{}|zuw(Zj`fBYqYSf@EJ^OSQo}H{%F~F@2GKW|$hi6iX6veCG z@rPr5h92IRM=BLS-ELs5>JRK*=Po4C2MbryC(G8;&=-2r%P&4n<^9XK+F-O|#T8Vl z5~L_jhyblKm1;8BU28UM-WH2IAf%^f(m$52rLQ;aqN(GCQxsE^0&LFOV}AukifRZE zkXx#wSLk9Mw@vjfzBNBi&mTk82G9)>nIzj}XV<^mdM}M;O^I`j3ZwuGdU=Z;R7=Q%2G$^s z9yx#p59mSff3%!$EZ`ab->`WPH4AOXM~eWA5`UAAkP-vdn9QCUM@^g5r}sYkw}h0B z7Jo%C(e3$)UjQT1CgXaMGC>F`5iK6EvY{{bqSUl>TKuoIZloaE{Mm|cY2w(IUFYOn zFH$B5f%gVv<~Syyg7~pk1WOVy2^C}0)*svrJnWZD>qW{0A%NCdHp*B0IvoTEJa%Xw z8Z%*@+bFq{lFE005H==&$0dY#N*ZN`5WrD3$~?9#n`=2j=ZeB6-Bx@dJ zVnlXA&@-_sj~YVV0=)f%ipgYjKOxiO%D(6`n;F$?vMRc_h-bF=mWO%*Fp(5PuI?i% zA(&(dvOw?-xd+6prklWPu~~X>otmy=os8;LRzke3BZvA%)-Xq#yY8=~z_);6DpM@p zm20+0dVjYr5pI(X{hu+o2sI$sPP}9JLG@~tU7uTRjvR!eR7m$gOLlNuB)>eflKT!E z;X62pEd=g4@VV}8?c}s|in+a$W-Q{W0Xy*oVq-Dqc@TpT2aX{K?9x!&@HciH@^qxI z{9)qsh2&|jpYI*zWawb~v z{y1LZDnPF*-VPG?16fB1{y3}6(c7PR>=9~MzYdQX0SJ4qVFaySznyQ89Mi1>b?(rX z*J~BUmdu|umX9>#GhAEvF@g>xB+@6Jt>gQ0d0r1ovOk>j7SEB0A$R%~kd+XWa0=U5 zVBf31O^c^tqoz}4R_<;<5Z1}x&hyv&g)6ym2lb1gxY+)Mx<^RkdX#cEjc*fp>dXZ? zmUx=>GuJlx+AV%4pq>GnW!g*?TUYOAeD2jYZTzeo>8uOktbv~>X@Bl zJ|NhVhg&zLZ&u8s#hiP(HUzt2$8>E^&oIX! zsE%~eDej_%y?YgLd$qjS+ELdlp1mW&iXDNXdC{7k&YZo(Y{41UWAlGZT)~DPa$&;s zFXzW6oWXT#afhHea}XX5Yf5!Rp%72N`$H#DLj+o}i6QI>QmR-hf{?tJ7{{Q{a0883 zL3rW0C#ip*uB12U`G#abVN$Lo5fvyd6fd?$AsW0dl-ih`3u_9qccK_@TMf=pbol3A zDJl5|T}ir5XU|{Za~iC`5(5PAy&8+S->)1{O~w%F0;M+a!W_Sj;P^c$kOQ(nkycZ% zet&n)KRFQsDpm+^R6#m_szR+Rrv#{m5HVaMha3dho!bKj)s@--&t4AOAtnSUZ!)OD zJ`@J3B*YUSh>4y0JrhkUR)5dt8M(17j3{9TI1o4R+3vJ^-%qL@1^J;=5CTBHEkf(l zjCcM*lV&XDUX`?shGL@H(QB_f$L2NbN+l_!h7bT=(ZIgZ)SzA+TK4583h?)rw81=L z!kEF-x>X3RTDP6Kz=ivf7qy^N5dy$wbO>)j5pA1uyW%aq3Q~G?jii_^5zb9+6nN14 zM5!YLaBS0&zmx^XptPY>67pw;j}Vo?M~KScBSdBJ5u!5q2vHe)gs2QYLR1DHAu5B9 j5S774h|1t2L}mB`i8!|Dx1IeW00000NkvXXu0mjfjGsU5 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png deleted file mode 100644 index e3f5d9d7361f301f37efcbb31a0fe93f86c02bf0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2040 zcmV782)Bsm;nM*6g4*#7u?bmQ*%3RCsMOA%o4Y<%rcL;mPcIjC}^gjl(?d$8KS1g z%oI-t_Z@d~-xW}CMQ|4E{{LfWNF|*2#xdeM=bahu%)4{H_b>PR|BLpa0E#%Yu3o^i zKrzXpf?|?I1;r$lKu(U>p;Ulcr7CdDnQ%JE#LauLV98p95ATPd>XmqG&%r+t8TA83 z59x!3^#Y-CGFhjS%*r-l+pdG?``On>TKhfpIvq?VGxqF1hCX6!>$=(S_VRG*8k|Ow zgp|daBV03w|Gs?n8jhW~fNB91U@}{I?AGnONIrZDO&iyNR-!iSqt_;P-j~7Oqz(kmoRqIgENO)M{mjDngP7 z-hW_WD`wu$!n{SlV9E0J{N5fjKl<=~Oi@(4Byu7??uhr+|dfDM1_ z#H4Q*@_7HSF2V(M(Ah1V!cOm|L)%uteC^tM1XH3Ha}vr?sMM83(qxggg1Dt?aV7N{ zzpq@eETSTZ!Dw*jEUD9|^S)0msZpz7A7)zdxu-$L+YNa|lSXy0bnZkjKZO#kERwV^ ziphl4iFsp#2lQZqdYy)^!mO0YlXZ!(l%3spT%%SOd}ATuFi9du67>AVt4K@F;Dze- zI<#)_GSea}J}r0-@nFc>l+1Jv%<(ulOwtxFwEx-CJS7#KR@&d>q<(f`5xYw10mt1n zHz!zxVzEK3mXwMTYg0-H5F93Hvy`{DXWrQ5Tva2|3%j$ktBb-y21)6a1UFfbgm~ut zY;4}1j5aMAai)|KkLw}Jl9V94JU!4fxGuKsJjmmbQHw;4SVLIYi$#J`dz~D}+CnIU zW|^!Q)W17+>^{uaym%=MVIPMhV%#7E2{CTXEwx+;StKb7s?|B7oT`Q~!#}`;Df2iM zPoKMt(7sU+k(q5q=$q}~U#29+O`e0aYZ*-T z#L4r8BAIO>+Ys;z2dP4Es8epY-($`>(S@E&gk8v6H?P|;Bd;Rf4zV7gcziz-(YWe zBcv7FgkaCiw!k2W>Rerg*n`~5AY50km12x)!Lt6P3PqCg?=ew}j*FOsIdQ8nc2s{Z zoU_=CRkG!s;yoTUyP6=It8KM~aJhD^gK_BSN$&qpH8WtyWKLw=-|8T_uSO>;DLJiM z$NQ-2$t@3ibbY-w+P7(fiBsoe^}5X<+fMvu0fK4-aOS4Q7(F;g;qqewt*|^<-@|CY zr0`F<0N<3fhxgprxD}W=WdysvUWQC`IY}m{*K*RLF5U3YdofAE_FadB78YVcrQssl zQIB;wNdj5OUFcjQN#$CVisf+j!WCpcFd_YV2Fm&S;h~IHfozi0=TV|8;u1{-wwSN) z3rv#6sH!2zQNXerS@(q6p=v{J*PAS5)AAWN@8pe_^!4FaSw*TbVcB z<+Ya5BIUMI9KO>I;ge>geA&|I-@7Y!$jPd-CK3Yn^<+-oN!QByjewEbu zD__Vx>%*OQbzsS9TidtwK% z`3F|5i0V}<;O4D6m^C*Z%T{dQ?|FI{Fe73pw>HTms48(8NiAtgVTI7H?XYHD66?m#@^(VN9OYJ_C(Ln5cGM!GYq1V zAzDUvWsdJ;$0Q{giZD-4BOklbAO=2l z>zqp!&2w`;X;UjY5jQ5Yh(A#a(or2<1Um4s$Gll%1PKiv-%(?_WC?F3Ot5*zbJrQL zf%Zp(UJZAVZK%SxUH{CC)L~CPbR0cD8p8r$VN5t$iU;za`kw(>BrVv+Ex17~jC4bh@CzP5i=HxY^>op^Y$`ge)MQen7V*1>ufmv z7s#@0nm0hJ+!(cuk%ia%IK|mN&v<5~*5fjzl7~74;kl?MosD9WMFqtqiwgc1B>w{a W$OzZ}i-srw0000X=H=lO8m*SXHQKi=ou=lVq&8|ktz0vRbNC|LAhP?LX-{(qvU`#0m} zLIWr$*i-bN8fHF}JGKlyES5Z%yD8c<*FlE?cF#oXCJ$|_i}wrrKCV}|ZY&q#QRW+t z?&8HG{N{Oe++}$tz*^d9_@jL&RLSMq*IuQxcm(K2)|t{?5v?YpM5 zrnK;;bc5Nceb0gGf{G4JM~8bDtU)xgXD4yogl^LO?{R@6Jtf76)diS7AW~g9;3Q8% zT=TFk4zcYSw7J_>LAE?r!$s$&^H=14C4L`kea3|1<<(Xw2~AS|vIT5@F4=YE6F<}N zQgiO1j;V2wIpqDR3QoOa8g%MMHnMJBdb>H_C=Abjkf4dBZYgiZoJhFOG-b9;S4TNo zdK8@8>oys#G`5W=F$oF>#UCte=SJj(BF_z|pWHwl`pylV#SN!1)F8i}|c9^q4JB5@)?^AP(ml z4C+%)2%d+*kf-+E4{rJ5*MQpNxtr#FkGs(MMHmuH!U7bnsfn$*hz4IWPkO2o22iV+ zU8SlMD25DC08PPEcFXGkf_K~@-&w=nY(6o{a(!Z_ISJKZx>SiZD~48WtE9U7h$u)V z4yr4d2PdJpGK2v%d1r=tUZ-Q0uLgSWts(h&*y3)hX%$W_tjU}kK|JP`*7D1%JSt+r zm&q%%q zQZ0X~eVbD7+?D?jwO~+|FW4CdGwcv7pvAV~w zg`+=bu3z%TG3(#NAy$e7@s(%iuMdD_=O~_l#$6?ezV7Klzc;y*0nH|DivmZ^>#GD# zhwU){SR3@BAc0Q2=Z{_B$A-eLAHjC-R=%#(D@2~zU66qTC%>4iYwpCZ8uDtHOC)>c z=z2KRO@Z^rQDHDWzzXbH&+3&2*FcFiOc0*~wWOHVXYM!MhhaeqZZ$WdX!{Qi-UZEbMn?5&n{~Icp!cUr zEr6=O^ngdqG*v;%rGI?{%@1BRve}KcB}9&tm4Vv!!$rPS z$UB}#?luZ&j%e??6WRW?`|kn0qGyKrsZ4XTp?1dSyCKC1^ieFFalmm2pR9#XiugB| zn24*pYO%gKk%qf2&evEzqJFzy5jgheOS>?~Q^?ELUxM$>=_C0#83<6vEF7e(ii4rn z{twU`AQTlknsCBFa#D~2$8&EWkowJdX37^5OHOOq5|d*UHZdtZh6P+D4L`PNC65D$ zZ&4Atuc1vZ3XS5P?G0KJXBIUK$ypH3tQ(OD!DkM^5m*5cWW1~riVQzq5F$aT9lZx)M{jxAZA zLfS~s`OR1@Sk!dqs2DN1*E`6k@F-Jwk9kr6{NRVag$^i3P<(I=+JHA@*93X9em^x& zTU@L%PW;BYli{}=CHq7RSxqzjX;{x!p-TL6?Lq-OpH^>+4&^Diw4%^okl9u-UDU|Vu~vSPucRk}3Z zrJux|1Z{~+iCzJGWhYHFNUkOVl45PC+wQ!Fb&J$NgAgeyhopA+px#gzn!ZH@|fX-5E{K zG76quaHU|^0rex^Snd1d;CWpy3i!>QnO*m^Zq!8j5v`KW9(4CLi?fD&V~$9K;X#Rt z`9ahTv_g!xy7UEzj$hMA`xBqmd?1qHgq|jWXc!E*oh<9E=M&Ixzj?A>qU^23KJpe31??irF?Q_W&B9>QIS^IRe+~Ls^0l}3A_EXV*UR(^cKELO5>NRK2UdbU{Fa&1^JCqn?R z=u{mhUqhGZR;}JBFT9@3AmqAV*;e~vMZ~&QHP*#o1!SKKsi~?mi=-bXS8C3!SUq_T z&!E4r1q!OyfJD7!Ai3$gT+=+&m6O+H-RTr+r>1(Lc(~nqEpBuxaqaO=MzYUv{>yA@ zW7kXeiY=m^u}4u%Bi<0l7{9_Oj)Nqvz9l9t_7{B>mY*1xiR`~? zR&1OxAK(dkgb8pT(Zy^U=1t!J#(lQOQ-eGhA2iyGj z^JsW|^0^OOt5OYP&sKp-j*vXn4fFfN8Da>2X`C1rQfZK5^hy0=H+;?3@=7&(`pH;A z&zMsKAt`te9TX41Y5P!>-L4$7cgHm{Sr!8+a~GL;_KC|^jsOI}rU>ddAl~~uXq@t4 zt$Uy4)8xS%l|Y;bJcdB3Ni{i}i19@~B6oWo1K8QHA}pxK59~xGvBhQ*ovA6WQtO`E z8PBr4MIQD@9?Qc~{P5NpbJvNaRNqO7aPG}p^t?ik8>es?imI*P7JeDJz_VDF#}U7N zapF@h$014&B3ZV~3yYe@i-d&Q98b<<)E9y-STku1#bc{a?xdJ#A{b^(P)uLd<*T@b zM=r1-Dj#=bwMVep`eHbGk0hQ#11NX?twdci*t2dH27ZMq&JIV|cZR=`5e!sHg{}bt z*Z8R?lFrF{TbPXzbNvV2!A4oiJwH9Ft?>*KJVY^Ozf-vi@pz)18)XNFZHQB3`BdCX zpmvn1Ux;WE;*-w`A*bqj=aF*zgA#9JYNBit(iRBu%iXNzxXE3c)Gy?JN-)G|H;@A@~=+wGMK11OiERLaB2?D(A3`snts>8+|a z{#q0AoYzaQVaNL(O9_hdTt6x{t&y*D8;^h35VvaUZ?X4*?U4b9Dnno-nFo1?=?r4! z+-IzuM}M$09_e9hZ2WO|J(33T=@Rj(tasa5fTd0Ay*p1_ZPtRzX7y0|+ye5BBYD8EiM}S?DT!WLl zw?)U5L9Qt09%aEr$FK2)(*NsvEFOE;hrcUJVN zHc~LCv0S>Y^7(CTb~N|gX9t_ausXBI+UKxbwN9DHBx)kYw^niB3d04d|4!Jqogg8C zXX;(QZB1{myn6&<2i#UC7sTXi2rVq9cePP;tz1M3A%90Dnt$xRxYZ;8FtN;c)U*EV zPj~mik>gQ01IvMQ8FowX11E7RtzqN4*MF|$`kW%z5|(QI!5V={kkYkrDfI8?w=a)= zRBEXBhwX;+pMMDk=ROkMoGb~c+$Lp(y^Khr$QN$+-+TJbQ>XPwtGYMz($wMqT=f4x c6*%X&%tnolo?Hd~0~Hi{+D6cFO?&MB0HVA(SO5S3 diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png deleted file mode 100644 index ec79fc46b62843e9c3dfddb922582cc57e6e9e8f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2717 zcmV;O3S#w%P)Fzyg5+BwQsd2qID&R#-tH>LO~`T@Des%4Io9B`gF%SOYAe zoU+8?C}e>JcDYeO4EGsskOV<;&6)k*>*;2aOq`_X&P>Yrs{YD!_e>vOzx%xyiUbfq znK1bJ2K*fXg1jI=kQW39@`3|IXce_-e~;?A&u08&{>GX-s{e-J@UFpxt}F#gSvD(LNzw+TqF$ALiy` z+3GErIcbE?q^ME}lI@P&LCdM^kE|Aso40dNV0G}e6OZ!etgE+>S7?J?r-NP}#8RfK zM6xP{pduG37MnvD;3CM+H*-n~qU+Q^cvz_Nz0$uWdnJ}2)W-ZdV9*7@WYjAvsUK zb~Zj+mVs4YevJ?R`X}`0)*e=&dIpD1NK+Leh$KfQ=j{0`j2t%$x9(&Y`%Xl7D0)8I z0g3HfA-2hrY>UD|Lztj{uG&=mCJhEeM1&z%cr`+9UIE5Vnu{-2e8g%;*CKV(0;&^4 zwnkPfA}2ZJZ+IvqNCpv;^2e7jFsT=Qy8kow9yo@TLL&P7HW7&(T6sKY^~eb9+qMMz z4t~$lbMTu?p7&^*l4R<1>PwNT1d*l`TJ8Lq?8eCVW)&mnH_aMj-pAt* zUAqQvzcy(OPMo^PpWC#IWrIr)idO2}Mj=JrySC$XR0Ni@=6#Msf+!gdN?Sbq?s1_i zLFBPyZ?$OYzu0ETXev};>4Fc0OAp~~eTlbCs%4T1D)F45W&_7fHnHd>v~3-a)k|hz z?S}1azu0bR)Ot{rAoeN(q&jOeb}0MxB3?aj=gg29+m8VyYASoD>2->#uAjd8;`u6IKCPIP;L=G8LlD3M1rOcp*Q4mgc&gLLn zCoxk}BGrP51U-`SBS%jbJJwqmV7h&J2!jgVhKLFgYNxjap-^}*im3fQ4syC;8Iq(d z5fI9t?dEYY(#7=Zno+V@K{`(+T|KC(Vg!1V4<6J(_KBl6Pe)RNu zoxa!+%CXF56RKj9TQ!1Y=^#m}5mgO0Z{L-V;QYm_Xy3L49Aw0e`ZCYqk5G*uIf^9} zqnIbt6#x=(+jkvef+!28P(Z7+Kntix5GR_FH|x?d0U4WjE5|l$+J#|*`=Md|Iw%m4 zwzEjDnOjyHss@T=Fs(vDdUR{Y$>;4mIlSLuwPDozv$6P-$y}z86WOW&RSBXXjtDXu zjTk>_7{*MTqr@j?FI>TZq2n=ObTaz%?Bo|Mo$^3cg2)b;43ZS}?vaSMULU~q8<~vk zocnlpN*X?0vK|{(&nfqLxtt-Slhm@ZiB-MaVHRGB5>{HH-X4s)b!y_{v=u0Hr5^Nv zm$GgMTUGv4gABWz=Mho#5_BpDon=9Ff`|ahzVj`1CTUPoFLX&v;Ou_$*L%5+^Ej}% zvU-vxdKuL1&{C*0sVK@MNeQJB^`dKG`s8;oCS?e={CgjE>^|(V=JQLKpT3SutS#ax zZ3$xX=_MHSBGaepfWPNrB_Su2Mk-IZ+C5QEL}J*>FYu};map2(QbRy zn&Yj}Gx5X83%sAA^cl04V)c@lyiF~qUmZczIi)y=RI^${7#5_ykN$(l;Qj-t?nLT# z`rKtSi;ZEUtrYTAK$RefB!cVE!W$A%HKHPsJmf{pTeOCc6B+vt9cO|FRNd77ng}8g z3Dh^UyIMcJyLUv|!qtkTP;@SX`T=TbS(PD(Kx9zlOe8gIP>0(|a<4x(FW>E(>Xj~4 zCy0%tcnB?mHg`ypLrWsVY%;nZRqZ+|x1Ni*sb_(Mqi8J#oMegl@$Nl191car>WXwm zhMKXWssynIaedC$V8Dq~OKvYwU*7$^En75EKC3Ccs`uszvYVC56jYv|9@gU|ltfN} zMXCf)@&EI6Ta{zf((dw1YyM2vP&08=SH)a^uU)@`U3qXbZo5PZnZIeF}r00!bQU({>h;^m6gk(YLSptGKF;To(deoAm@6{*CXE^@*H&A4_`GVKjNYDERIBfxs_{z9=`8qCKx`q$N4=?rm{~b7rNz)ec1T2luc#;N_h|Jk%i*|u7($j*7d1LW=?S8jgHxT?I+G+8CD7Glz&$XKNG$|%UA ziB$4TWpy?(Ht%AOmGjdBVXPC7o;HcY1Tw@%4X4RGp^|ITP{v=#t93p-@<;T`&zAd;GA+BQS`Hu0q*>nNdSQ{Q`? z%NxF&1M!QBBHI@aEN}%;@7pmMzaYq;5g^D50t9(MfFLgj5aa~`g1jI=kQW39@`C>Z XST`OXJ_6%R00000NkvXXu0mjfT=*NC diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png deleted file mode 100644 index 1ef942f3bc1d1d300c8830d094c789d6e83a76cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5611 zcmbtYRa6vQxSgRx7^Fl%7&;`ROJFGJ8UbmLZb@P2R6vlShVF);hDK6E>F(|hY3a-V zd|&Qb_u-uL@a^-h{jk6F?X@E`)Z_{9DDeOQ0D+={EcEGx|95b(p6=~;YmEQ^8A?%B zO3Mp)n1Ss@I!XRVy*{5PGMLDl8;XG*O1}y0!-tY-#Da)qF?>O*;z2BwUQju?0-SKI z5;Q0&MgqGWDY|CcRlJiLShB^h0jp|v|H3BaCNi<=FYRhse5+@W`w?=BP3?JSm8m3 zFGNO}t)4%X=aWEK0*9KTfj};bj;Uwa>=Xv~B?j+wtUcmCZ>*wG}JuU(JkXz3r0A5ds*oHb97#kRF zzClvWteEM>cAs3xbAPIHV1(OJGf}E&l;!Z-7YiJ^5(rooqi5$dYW9r|yB>R~ zc3Zd7@$fCFLbL0pE_pIeC_X0i29pwu6^(&@<9u*0T=8ecqlOel7EsF?mcXtSGmShQ znXWD2l_&w0_Gf#;&*j;S|!pi z@$m38FjZn4h&2LV$3kDq>U=Bd@Q@~F4Wtl`D^QFZfE3>5o**-A$7NldriM|sAA8chvlRrrab;M6w)c$8E0GY}DyutQOC@^h4>(GMEcAD41t zoig%lynCdJF+T7^^w9B)_fG{Gs`hyq&QyVHmOU-&M!e1w0O z|M0F%hl@bjhkUFHJB>j*vJ$iFF)h&zo~aTFYHG$#b1S-57FxT3!Fhh0e-?9%W|ZPL z3Znz}rY{grb9OmXcPjk_$SS~~@`0U=EoCY4UqbZ9&2y=)lEb-^Pe|@!-O-;kKjZj{*@T)`xdA1ACCOfHbKY5KVVnV z{z<`?fACyDL>$3$JJ(29neFzH$)Y}uaA)3^iyR4V4|fr#n+5IKqxp=K_B85r%l4?p zYs=94-Go~yly_a(C=w(;?t;r`c-Fz)ZoSk$92-y!NKg||wqBPpM8_zLFJ9xP74?nU z;$GY#&-@ujayKiV$Az!s6(DVp_TU09p`TH3~+9) zj#R?%vBeI%RVi%do(hxPZa5IqsS7p-`L0;dcAX5M*cOVy26W z@yUu;ib&)ZpYRAbLjdkz@tI%ezrU`PB4kN`64jjR^_)=7imm# z4B3kOGS#~NA^>X6f!fo~ivE$xfeD&;kVc=!pn11Qve#?pKVs80eRiDEZ#_t#2;<67 z99i(_(x^(segypNX%?e5@rTEDvq0EEuV8&nL)kUkj}~ks-=(b?@u+cN>V|0f)P!gnn|%nVWb(oD4;B-+ zNm5!-;wLnHR7SpNjL$={24eFlwO==iW3%P42~z2C>IKC4wIv4jq&7?(Ic7ipLR|1;x&r!}EL(vd12z!}zHKSW$_ zK}YEWPnHe>lI!Hj_tdNrgK4MvdMQ#P4r+mlrM_Ddhmr>ff2Ze5(D zCS_VXMwM9o4=e5OB_R(?}^_g}}Aaqy;;NV)aM>){%D zHP9M~z_6UC!mo`c_l+GkZy(tJcEd*aR<-oalRSy6G#J^4$7o-lutwhwgDJ(G3g?Zj zTIxTZ(aia*;M|{GBo+H4)mv0M-)~bw4Fhrm!5zy72~F5mqF)a_Pjf%rXt?sOm@v%kBzuI_nTUm9i za$NY@VRKEfbb#Q$s#vnP>_ouqi+;)6?*4X5?Y=dVT87%YIMj%jREh2RZ?*Lx!+lCQYTLUhq$G3IH1&I-}tAXf|QPDj;~q@Ct%| z#yP?61?h=>cY*IfQL$vHjO6t#%rv0UHw%a5qz7 z)$a5=6)_%02oVi=siQ9E<@J^zvME4s?lB@jY#z(*DGnE0s zPGZszjfU^#i(4LDxw*xOtf-VfKU+$rf&Lu6U*!6B&0fRkAm7<7p2u&Eq=(tthHvDF zb4wvHiQ8}*{TM8tQP6;KKzQ0qqY+=3ffrs5`u18|A}=)@~yw| z0!kGtLL5oM@h~8$zu6m^D+7smOtcX%%0h2tOeO;ZqKb?c=f`(eD>Wd2?fou+3P+(E zMwobD`OZynRZ2GOM*f-8M&<5MaV$s{BiEIA-E={h=*?T&FpE68{F)4?7<?{>3C+hpoO5}w)bAjbIt24f<0x-0F=iqu=LhTn?<+E9j%#0ctakr zp|}d1->1>ni!9SGx94!FvJS|%G9g7|QHr-@jqk58Cysmk!hjgVTdNxNf_s+oMexRJ zs)I-9qCJL&j9U5%Imqx_E580x{?rV8GE0g>aH8L>4cM@poo;i+Du!P3I#g-lM>IBi zCHbY-o1%d~;oq@-v;2rbW03nUi%66(n*InF_riD;_IBrEcd7J`h+ZD44%Rgquq;n8 zFLULOZgcBYv4S#=fC#9sV(;D*wn?%)soXf7@uSnz#}6-lWz*A>4w$Z7bZN!ADhZOh zoe8EP$_3?V0g0UN$1Ekk8bMSH(L)uwc?8I&a5nd5TotkOl2yz3PBv^0Nn;?rLhK)Y z*U@uUnow2%p$4^r(UWia-^G7vvauuI+3qngvEq~1kBuj+k&YJlvIj3_pQId`p-`E8?%QU`EA?&^c@hg{%Z=+-h2=W zXwTy8&-Tt5gS~oOk|v`ucFPmN3O5whsD)B)*#7(%hsGEcwf#aN5bJMoCoOGOhaIWy zqCpL_y2fPR=F{3X%uF^3>=Af~7WGcO#=h8}(h$cUeipH#n9!$GqRWR!9Kc5a-frcw z*^7$q<)!rNVifxOixTWzwd~pdrzqNoo@zeb#Oj>N%L$Imh+~Q0Syd_2TamDu{7C&n zdU?m0mNcDDGZnD=su9r%jsXFxQR8yN@D~2lmhv6xV`Lx53MOf`*tDAaw;03kZVK`~ zX}VNP+)Y$6ANN8Po)&ZFt(3YKSqkadmzs0fEqAg~3Rz|{znzgh9c(MDJduz|{Et6+ zrB1XG5?976Dw68Ggs}>KlAm(r<*u}Nv&3PXOkQDnzFf~#|E|BQZBza6b=U)+Q{kS{ z1QF!bLc7@OxB^mx`gHSs^b>q|)_s?o`#YdQ7go%8rM#L2O-T6Gy*i@GgIA;P#pFSm z7W?+3PkcFv=v`{qr1@38mb5gzAFmH)`Q43A!|{dt{+pZaeeTjWavK4+=i1FA4FIXX z1{&rgn!}z>X}E9>-=3@WwcUR0V#>oo>=y+v8VAuYMpk!LK*AB z2|!*l(+`n3x|A9rUb$T=ywz|TLHaK6BgY{15O7$jn$MpnsCiYI;rGjJ4i^ebZ zc}PiolFZDU-kU**(P4MVbX z`AR%~h#g8sj$oUuAO(zj`4r^5@xHF}WE1`Ew?v4Mt(3_6Zk+jhcFrf&f&|hl zk&!m~EKHoy_mhzYyJcbTZ3aST&(A6YEU4H%7Rt|)=hS*#3VU0xQ8&9^dj~~VxUxT? zrE9cQgK_M&sju%z5AWF`kvhJNbE}d!Zu+56nkeye+Z(AU@q5UN(WlSCRv^D|R6(T6blmvOAaTv}Grj+R h(dPdONXH(5Lc*q`A&%caJcYObML9Lu3Te~e{{d>Xo|OOq diff --git a/lib/model/transaction.dart b/lib/model/transaction.dart index c5aaebc3..05969caf 100644 --- a/lib/model/transaction.dart +++ b/lib/model/transaction.dart @@ -16,6 +16,7 @@ class TransactionFields extends BaseEntityFields { static String categoryName = 'categoryName'; static String categoryColor = 'categoryColor'; static String categorySymbol = 'categorySymbol'; + static String categoryParent = 'categoryParent'; static String idBankAccount = 'idBankAccount'; // FK static String bankAccountName = 'bankAccountName'; static String idBankAccountTransfer = 'idBankAccountTransfer'; @@ -95,6 +96,7 @@ class Transaction extends BaseEntity { final String? categoryName; final int? categoryColor; final String? categorySymbol; + final int? categoryParent; final int idBankAccount; final String? bankAccountName; final int? idBankAccountTransfer; @@ -112,6 +114,7 @@ class Transaction extends BaseEntity { this.categoryName, this.categoryColor, this.categorySymbol, + this.categoryParent, required this.idBankAccount, this.bankAccountName, this.idBankAccountTransfer, @@ -162,6 +165,7 @@ class Transaction extends BaseEntity { categoryName: json[TransactionFields.categoryName] as String?, categoryColor: json[TransactionFields.categoryColor] as int?, categorySymbol: json[TransactionFields.categorySymbol] as String?, + categoryParent: json[TransactionFields.categoryParent] as int?, idBankAccount: json[TransactionFields.idBankAccount] as int, bankAccountName: json[TransactionFields.bankAccountName] as String?, idBankAccountTransfer: diff --git a/lib/pages/planning/widget/budget_card.dart b/lib/pages/planning/widget/budget_card.dart index ea8a22c9..f7f3ddf0 100644 --- a/lib/pages/planning/widget/budget_card.dart +++ b/lib/pages/planning/widget/budget_card.dart @@ -6,7 +6,6 @@ import '../../../model/category_transaction.dart'; import '../../../providers/categories_provider.dart'; import '../../../ui/extensions.dart'; import '../../../ui/widgets/default_container.dart'; -import '../../../model/budget.dart'; import '../../../providers/budgets_provider.dart'; import '../../../providers/currency_provider.dart'; import '../../../providers/transactions_provider.dart'; @@ -54,17 +53,17 @@ class BudgetCard extends ConsumerWidget { physics: const NeverScrollableScrollPhysics(), itemCount: budgets.length, itemBuilder: (BuildContext context, int index) { + final budget = budgets[index]; num spent = num.parse( transactions .where( (t) => - t.idCategory == - budgets[index].idCategory, + t.idCategory == budget.idCategory || + t.categoryParent == budget.idCategory, ) .fold(0.0, (sum, t) => sum + t.amount) .toCurrency(), ); - Budget budget = budgets.elementAt(index); CategoryTransaction category = categories .firstWhere( (cat) => cat.id == budget.idCategory, diff --git a/lib/pages/transactions/transactions_page.dart b/lib/pages/transactions/transactions_page.dart index d1e20d17..8534c518 100644 --- a/lib/pages/transactions/transactions_page.dart +++ b/lib/pages/transactions/transactions_page.dart @@ -58,62 +58,67 @@ class _TransactionsPageState extends ConsumerState ); final transactionsExistsAsync = ref.watch(transactionsExistsProvider); - return transactionsExistsAsync.when( - data: (transactionsExists) { - if (transactionsExists) { - return NotificationListener( - onNotification: (notification) { - // snap the header open/close when it's in between the two states - final double scrollDistance = headerMaxHeight - headerMinHeight; + return NotificationListener( + onNotification: (notification) { + // snap the header open/close when it's in between the two states + final double scrollDistance = headerMaxHeight - headerMinHeight; - if (_scrollController.offset > 0 && - _scrollController.offset < scrollDistance) { - final double snapOffset = - (_scrollController.offset / scrollDistance > 0.5) - ? scrollDistance + 10 - : 0; + if (_scrollController.offset > 0 && + _scrollController.offset < scrollDistance) { + final double snapOffset = + (_scrollController.offset / scrollDistance > 0.5) + ? scrollDistance + 10 + : 0; - //! the app freezes on animateTo - // // Future.microtask(() => _scrollController.animateTo(snapOffset, - // // duration: Duration(milliseconds: 200), curve: Curves.easeIn)); + //! the app freezes on animateTo + // // Future.microtask(() => _scrollController.animateTo(snapOffset, + // // duration: Duration(milliseconds: 200), curve: Curves.easeIn)); - // microtask() runs the callback after the build ends - Future.microtask(() => _scrollController.jumpTo(snapOffset)); - } - return false; - }, - child: NestedScrollView( - controller: _scrollController, - headerSliverBuilder: (context, innerBoxIsScrolled) { - return [ - SliverPersistentHeader( - delegate: CustomSliverDelegate( - ticker: this, - myTabs: myTabs, - tabController: _tabController, - expandedHeight: headerMaxHeight, - minHeight: headerMinHeight, - ), - pinned: true, - floating: true, + // microtask() runs the callback after the build ends + Future.microtask(() => _scrollController.jumpTo(snapOffset)); + } + return false; + }, + child: transactionsExistsAsync.when( + data: (transactionsExists) { + if (transactionsExists) { + return LayoutBuilder( + key: ValueKey(transactionsExists), + builder: (context, constraints) { + return NestedScrollView( + controller: _scrollController, + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverPersistentHeader( + delegate: CustomSliverDelegate( + ticker: this, + myTabs: myTabs, + tabController: _tabController, + expandedHeight: headerMaxHeight, + minHeight: headerMinHeight, + ), + pinned: true, + floating: true, + ), + ]; + }, + body: TabBarView( + controller: _tabController, + children: const [ListTab(), CategoriesTab(), AccountsTab()], ), - ]; + ); }, - body: TabBarView( - controller: _tabController, - children: const [ListTab(), CategoriesTab(), AccountsTab()], - ), - ), - ); - } + ); + } - return const AddTransactionCard(); - }, - loading: () => const Center(child: CircularProgressIndicator()), - error: (error, stack) => Center( - child: Text( - "An error occurred: $error", - style: Theme.of(context).textTheme.bodySmall, + return const AddTransactionCard(); + }, + loading: () => const Center(child: CircularProgressIndicator()), + error: (error, stack) => Center( + child: Text( + "An error occurred: $error", + style: Theme.of(context).textTheme.bodySmall, + ), ), ), ); diff --git a/lib/providers/categories_provider.dart b/lib/providers/categories_provider.dart index 5bab7cb3..da456b6b 100644 --- a/lib/providers/categories_provider.dart +++ b/lib/providers/categories_provider.dart @@ -234,7 +234,11 @@ Future> categoryMap(Ref ref) async { for (var category in categories) { final sum = transactions - .where((transaction) => transaction.idCategory == category.id) + .where( + (transaction) => + transaction.idCategory == category.id || + transaction.categoryParent == category.id, + ) .fold( 0.0, (previousValue, transaction) => previousValue + transaction.amount, diff --git a/lib/providers/transactions_provider.dart b/lib/providers/transactions_provider.dart index 3b89572a..fcbf8182 100644 --- a/lib/providers/transactions_provider.dart +++ b/lib/providers/transactions_provider.dart @@ -155,6 +155,7 @@ class TransactionsNotifier extends _$TransactionsNotifier { ref.invalidate(monthlyTransactionsProvider); ref.invalidate(dashboardProvider); ref.invalidate(statisticsProvider); + ref.invalidate(categoryMapProvider); final dateStart = ref.watch(filterDateStartProvider); final dateEnd = ref.watch(filterDateEndProvider); final transactions = await ref diff --git a/lib/services/database/repositories/transactions_repository.dart b/lib/services/database/repositories/transactions_repository.dart index fb85b495..410bf646 100644 --- a/lib/services/database/repositories/transactions_repository.dart +++ b/lib/services/database/repositories/transactions_repository.dart @@ -102,7 +102,7 @@ class TransactionsRepository { final orderByDESC = '${TransactionFields.date} DESC'; final result = await db.rawQuery( - 'SELECT t.*, c.${CategoryTransactionFields.name} as ${TransactionFields.categoryName}, c.${CategoryTransactionFields.color} as ${TransactionFields.categoryColor}, c.${CategoryTransactionFields.symbol} as ${TransactionFields.categorySymbol}, b1.${BankAccountFields.name} as ${TransactionFields.bankAccountName}, b2.${BankAccountFields.name} as ${TransactionFields.bankAccountTransferName} FROM "$transactionTable" as t LEFT JOIN $categoryTransactionTable as c ON t.${TransactionFields.idCategory} = c.${CategoryTransactionFields.id} LEFT JOIN $bankAccountTable as b1 ON t.${TransactionFields.idBankAccount} = b1.${BankAccountFields.id} LEFT JOIN $bankAccountTable as b2 ON t.${TransactionFields.idBankAccountTransfer} = b2.${BankAccountFields.id} ${where != null ? "WHERE $where" : ""} ORDER BY $orderByDESC ${limit != null ? "LIMIT $limit" : ""}', + 'SELECT t.*, c.${CategoryTransactionFields.name} as ${TransactionFields.categoryName}, c.${CategoryTransactionFields.color} as ${TransactionFields.categoryColor}, c.${CategoryTransactionFields.symbol} as ${TransactionFields.categorySymbol}, c.${CategoryTransactionFields.parent} as ${TransactionFields.categoryParent}, b1.${BankAccountFields.name} as ${TransactionFields.bankAccountName}, b2.${BankAccountFields.name} as ${TransactionFields.bankAccountTransferName} FROM "$transactionTable" as t LEFT JOIN $categoryTransactionTable as c ON t.${TransactionFields.idCategory} = c.${CategoryTransactionFields.id} LEFT JOIN $bankAccountTable as b1 ON t.${TransactionFields.idBankAccount} = b1.${BankAccountFields.id} LEFT JOIN $bankAccountTable as b2 ON t.${TransactionFields.idBankAccountTransfer} = b2.${BankAccountFields.id} ${where != null ? "WHERE $where" : ""} ORDER BY $orderByDESC ${limit != null ? "LIMIT $limit" : ""}', ); return result.map((json) => Transaction.fromJson(json)).toList(); diff --git a/lib/services/notifications/notifications_service.dart b/lib/services/notifications/notifications_service.dart index f9355b70..e9d529cd 100644 --- a/lib/services/notifications/notifications_service.dart +++ b/lib/services/notifications/notifications_service.dart @@ -28,7 +28,7 @@ class NotificationService { android: initializeSettingsAndroid, iOS: initializeSettingsIOS, ); - await notificationsPlugin.initialize(initializationSettings); + await notificationsPlugin.initialize(settings: initializationSettings); await notificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin @@ -141,17 +141,17 @@ class NotificationService { // scheduledDate = now.add(const Duration(seconds: 10)); await notificationsPlugin.zonedSchedule( - id, - title, - body, - tz.TZDateTime.from(scheduledDate, tz.local), - notificationDetails, + id: id, + title: title, + body: body, + scheduledDate: tz.TZDateTime.from(scheduledDate, tz.local), + notificationDetails: notificationDetails, androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, matchDateTimeComponents: matchDateTimeComponents, ); } static Future cancelNotification({int id = 0}) async { - await notificationsPlugin.cancel(id); + await notificationsPlugin.cancel(id: id); } } diff --git a/pubspec.lock b/pubspec.lock index 24d63608..f9865848 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "91.0.0" + analysis_server_plugin: + dependency: transitive + description: + name: analysis_server_plugin + sha256: "26844e7f977087567135d62532b67d5639fe206c5194c3f410ba75e1a04a2747" + url: "https://pub.dev" + source: hosted + version: "0.3.3" analyzer: dependency: transitive description: @@ -77,18 +85,18 @@ packages: dependency: transitive description: name: build - sha256: "7174c5d84b0fed00a1f5e7543597b35d67560465ae3d909f0889b8b20419d5e3" + sha256: "275bf6bb2a00a9852c28d4e0b410da1d833a734d57d39d44f94bfc895a484ec3" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "4.0.4" build_config: dependency: transitive description: name: build_config - sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.2.0" build_daemon: dependency: transitive description: @@ -97,30 +105,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "82730bf3d9043366ba8c02e4add05842a10739899520a6a22ddbd22d333bd5bb" - url: "https://pub.dev" - source: hosted - version: "3.0.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: "32c6b3d172f1f46b7c4df6bc4a47b8d88afb9e505dd4ace4af80b3c37e89832b" - url: "https://pub.dev" - source: hosted - version: "2.6.1" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "4b188774b369104ad96c0e4ca2471e5162f0566ce277771b179bed5eabf2d048" + sha256: b4d854962a32fd9f8efc0b76f98214790b833af8b2e9b2df6bfc927c0415a072 url: "https://pub.dev" source: hosted - version: "9.2.1" + version: "2.10.5" built_collection: dependency: transitive description: @@ -153,14 +145,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" - ci: - dependency: transitive - description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" - url: "https://pub.dev" - source: hosted - version: "0.1.0" cli_util: dependency: transitive description: @@ -257,38 +241,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" - custom_lint: - dependency: transitive - description: - name: custom_lint - sha256: "751ee9440920f808266c3ec2553420dea56d3c7837dd2d62af76b11be3fcece5" - url: "https://pub.dev" - source: hosted - version: "0.8.1" - custom_lint_builder: - dependency: transitive - description: - name: custom_lint_builder - sha256: "1128db6f58e71d43842f3b9be7465c83f0c47f4dd8918f878dd6ad3b72a32072" - url: "https://pub.dev" - source: hosted - version: "0.8.1" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: "85b339346154d5646952d44d682965dfe9e12cae5febd706f0db3aa5010d6423" - url: "https://pub.dev" - source: hosted - version: "0.8.1" - custom_lint_visitor: - dependency: transitive - description: - name: custom_lint_visitor - sha256: "91f2a81e9f0abb4b9f3bb529f78b6227ce6050300d1ae5b1e2c69c66c7a566d8" - url: "https://pub.dev" - source: hosted - version: "1.0.0+8.4.0" dart_style: dependency: transitive description: @@ -365,10 +317,10 @@ packages: dependency: "direct main" description: name: file_picker - sha256: "7872545770c277236fd32b022767576c562ba28366204ff1a5628853cf8f2200" + sha256: "57d9a1dd5063f85fa3107fb42d1faffda52fdc948cefd5fe5ea85267a5fc7343" url: "https://pub.dev" source: hosted - version: "10.3.7" + version: "10.3.10" fixnum: dependency: transitive description: @@ -410,34 +362,34 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "19ffb0a8bb7407875555e5e98d7343a633bb73707bae6c6a5f37c90014077875" + sha256: "76cd20bcfa72fabe50ea27eeaf165527f446f55d3033021462084b87805b4cac" url: "https://pub.dev" source: hosted - version: "19.5.0" + version: "20.0.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: e3c277b2daab8e36ac5a6820536668d07e83851aeeb79c446e525a70710770a5 + sha256: dce0116868cedd2cdf768af0365fc37ff1cbef7c02c4f51d0587482e625868d0 url: "https://pub.dev" source: hosted - version: "6.0.0" + version: "7.0.0" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "277d25d960c15674ce78ca97f57d0bae2ee401c844b6ac80fcd972a9c99d09fe" + sha256: "23de31678a48c084169d7ae95866df9de5c9d2a44be3e5915a2ff067aeeba899" url: "https://pub.dev" source: hosted - version: "9.1.0" + version: "10.0.0" flutter_local_notifications_windows: dependency: transitive description: name: flutter_local_notifications_windows - sha256: "8d658f0d367c48bd420e7cf2d26655e2d1130147bca1eea917e576ca76668aaf" + sha256: "7ddd964fa85b6a23e96956c5b63ef55cdb9e5947b71b95712204db42ad46da61" url: "https://pub.dev" source: hosted - version: "1.0.3" + version: "2.0.0" flutter_native_splash: dependency: "direct main" description: @@ -466,10 +418,10 @@ packages: dependency: "direct main" description: name: flutter_riverpod - sha256: "9e2d6907f12cc7d23a846847615941bddee8709bf2bfd274acdf5e80bcf22fde" + sha256: "38ec6c303e2c83ee84512f5fc2a82ae311531021938e63d7137eccc107bf3c02" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -528,14 +480,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b - url: "https://pub.dev" - source: hosted - version: "4.3.0" html: dependency: transitive description: @@ -948,58 +892,50 @@ packages: dependency: transitive description: name: riverpod - sha256: c406de02bff19d920b832bddfb8283548bfa05ce41c59afba57ce643e116aa59 + sha256: "16ff608d21e8ea64364f2b7c049c94a02ab81668f78845862b6e88b71dd4935a" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.1.0" riverpod_analyzer_utils: dependency: transitive description: name: riverpod_analyzer_utils - sha256: a0f68adb078b790faa3c655110a017f9a7b7b079a57bbd40f540e80dce5fcd29 + sha256: "947b05d04c52a546a2ac6b19ef2a54b08520ff6bdf9f23d67957a4c8df1c3bc0" url: "https://pub.dev" source: hosted - version: "1.0.0-dev.7" + version: "1.0.0-dev.8" riverpod_annotation: dependency: "direct main" description: name: riverpod_annotation - sha256: "7230014155777fc31ba3351bc2cb5a3b5717b11bfafe52b1553cb47d385f8897" + sha256: cc1474bc2df55ec3c1da1989d139dcef22cd5e2bd78da382e867a69a8eca2e46 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "4.0.0" riverpod_generator: dependency: "direct dev" description: name: riverpod_generator - sha256: "49894543a42cf7a9954fc4e7366b6d3cb2e6ec0fa07775f660afcdd92d097702" + sha256: e43b1537229cc8f487f09b0c20d15dba840acbadcf5fc6dad7ad5e8ab75950dc url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "4.0.0+1" riverpod_lint: dependency: "direct dev" description: name: riverpod_lint - sha256: "7ef9c43469e9b5ac4e4c3b24d7c30642e47ce1b12cd7dcdd643534db0a72ed13" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + sha256: "4d2eb0d19bbe7e3323bd0ce4553b2e6170d161a13914bfdd85a3612329edcb43" url: "https://pub.dev" source: hosted - version: "0.28.0" + version: "3.1.0" shared_preferences: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: @@ -1277,14 +1213,6 @@ packages: url: "https://pub.dev" source: hosted version: "0.10.1" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" - url: "https://pub.dev" - source: hosted - version: "1.0.2" typed_data: dependency: transitive description: @@ -1365,14 +1293,6 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.4" - uuid: - dependency: transitive - description: - name: uuid - sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 - url: "https://pub.dev" - source: hosted - version: "4.5.2" vector_math: dependency: transitive description: @@ -1469,6 +1389,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + yaml_edit: + dependency: transitive + description: + name: yaml_edit + sha256: ec709065bb2c911b336853b67f3732dd13e0336bd065cc2f1061d7610ddf45e3 + url: "https://pub.dev" + source: hosted + version: "2.2.3" sdks: - dart: ">=3.10.1 <4.0.0" + dart: ">=3.10.7 <4.0.0" flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index ea9eb0e3..6b6c2c02 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -3,10 +3,10 @@ description: A wealth management / personal finance / net worth tracking app. publish_to: "none" -version: 0.3.0+101 +version: 0.3.2+103 environment: - sdk: ^3.10.1 + sdk: ^3.10.7 dependencies: flutter: @@ -17,12 +17,12 @@ dependencies: csv: ^6.0.0 cupertino_icons: ^1.0.8 device_info_plus: ^12.3.0 - file_picker: ^10.3.7 + file_picker: ^10.3.10 fl_chart: ^1.1.1 - flutter_local_notifications: ^19.5.0 + flutter_local_notifications: ^20.0.0 flutter_native_splash: ^2.4.7 flutter_phoenix: ^1.1.1 - flutter_riverpod: ^3.0.3 + flutter_riverpod: ^3.1.0 intl: ^0.20.2 local_auth: ^3.0.0 package_info_plus: ^9.0.0 @@ -30,8 +30,8 @@ dependencies: path_provider: ^2.1.5 percent_indicator: ^4.2.5 permission_handler: ^12.0.1 - riverpod_annotation: ^3.0.3 - shared_preferences: ^2.5.3 + riverpod_annotation: ^4.0.0 + shared_preferences: ^2.5.4 sqflite: ^2.4.2 sqflite_common_ffi: ^2.4.0 sqlite3_flutter_libs: @@ -42,14 +42,14 @@ dev_dependencies: flutter_test: sdk: flutter - build_runner: ^2.6.1 + build_runner: ^2.10.5 dependency_validator: ^5.0.3 flutter_launcher_icons: ^0.14.4 flutter_lints: ^6.0.0 freezed: ^3.2.3 json_serializable: ^6.11.2 - riverpod_generator: ^3.0.3 - riverpod_lint: ^3.0.3 + riverpod_generator: ^4.0.0+1 + riverpod_lint: ^3.1.0 test: ^1.26.3 flutter: From 5af1310aa430d33101af5cf2b2b751d711f20e96 Mon Sep 17 00:00:00 2001 From: "Mike V." <113628339+mikev-cw@users.noreply.github.com> Date: Sat, 21 Feb 2026 19:54:21 +0100 Subject: [PATCH 06/20] Add changelog for version 1034 --- metadata/en-US/changelogs/{1014.txt => 1034.txt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename metadata/en-US/changelogs/{1014.txt => 1034.txt} (97%) diff --git a/metadata/en-US/changelogs/1014.txt b/metadata/en-US/changelogs/1034.txt similarity index 97% rename from metadata/en-US/changelogs/1014.txt rename to metadata/en-US/changelogs/1034.txt index b5589e41..c2f9f490 100644 --- a/metadata/en-US/changelogs/1014.txt +++ b/metadata/en-US/changelogs/1034.txt @@ -13,4 +13,4 @@ ...along with many other small improvements and bug fixes! IMPORTANT NOTE: This is still a beta release and includes significant internal changes. -Before updating, please make a backup of your database to avoid any potential data loss. \ No newline at end of file +Before updating, please make a backup of your database to avoid any potential data loss. From c66e8c3ea9b7126ffcfcfa8ada8e16db1013d341 Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Thu, 26 Feb 2026 19:56:29 +0100 Subject: [PATCH 07/20] Settings redesign --- lib/pages/accounts/account_list_page.dart | 31 +-- lib/pages/categories/category_list_page.dart | 31 +-- lib/pages/settings/backup/backup_page.dart | 13 +- .../settings/infos/collaborators_page.dart | 196 ++++++++++++++---- lib/pages/settings/infos/more_info_page.dart | 3 +- .../notifications/notifications_settings.dart | 31 +-- lib/pages/settings/settings_page.dart | 103 +++++---- pubspec.lock | 8 + pubspec.yaml | 1 + 9 files changed, 233 insertions(+), 184 deletions(-) diff --git a/lib/pages/accounts/account_list_page.dart b/lib/pages/accounts/account_list_page.dart index 47b71c42..e22b2616 100644 --- a/lib/pages/accounts/account_list_page.dart +++ b/lib/pages/accounts/account_list_page.dart @@ -26,6 +26,7 @@ class _AccountListPage extends ConsumerState { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), + title: const Text('Accounts'), actions: [ IconButton( onPressed: () { @@ -38,38 +39,10 @@ class _AccountListPage extends ConsumerState { ], ), body: SingleChildScrollView( + padding: const EdgeInsets.only(top: Sizes.xl), physics: const BouncingScrollPhysics(), child: Column( children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: Sizes.xl, - horizontal: Sizes.lg, - ), - child: Row( - children: [ - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.primary, - ), - padding: const EdgeInsets.all(Sizes.sm), - child: Icon( - Icons.account_balance_wallet, - size: 24.0, - color: Theme.of(context).colorScheme.onPrimary, - ), - ), - const SizedBox(width: Sizes.md), - Text( - "Your accounts", - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ), accountsList.when( data: (accounts) => ReorderableListView.builder( shrinkWrap: true, diff --git a/lib/pages/categories/category_list_page.dart b/lib/pages/categories/category_list_page.dart index 431dddc1..1fd87da9 100644 --- a/lib/pages/categories/category_list_page.dart +++ b/lib/pages/categories/category_list_page.dart @@ -21,6 +21,7 @@ class CategoryList extends ConsumerWidget { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), + title: const Text('Categories'), actions: [ IconButton( onPressed: () { @@ -33,38 +34,10 @@ class CategoryList extends ConsumerWidget { ], ), body: SingleChildScrollView( + padding: const EdgeInsets.only(top: Sizes.xl), physics: const BouncingScrollPhysics(), child: Column( children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: Sizes.xl, - horizontal: Sizes.lg, - ), - child: Row( - children: [ - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.primary, - ), - padding: const EdgeInsets.all(Sizes.sm), - child: Icon( - Icons.list_alt, - size: 24.0, - color: Theme.of(context).colorScheme.onPrimary, - ), - ), - const SizedBox(width: Sizes.md), - Text( - "Your categories", - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ), categorysList.when( data: (categorys) => ReorderableListView.builder( shrinkWrap: true, diff --git a/lib/pages/settings/backup/backup_page.dart b/lib/pages/settings/backup/backup_page.dart index c38b23e2..11ea00fc 100644 --- a/lib/pages/settings/backup/backup_page.dart +++ b/lib/pages/settings/backup/backup_page.dart @@ -122,22 +122,15 @@ class _BackupPageState extends ConsumerState { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - backgroundColor: Theme.of(context).colorScheme.onPrimary, - elevation: 0, - centerTitle: true, leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: Text( - 'Manage your data', - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), + title: const Text('Import/Export'), ), body: ListView.separated( - padding: const EdgeInsets.symmetric(vertical: Sizes.lg), + padding: const EdgeInsets.only(top: Sizes.xl), + physics: const BouncingScrollPhysics(), itemCount: options.length, separatorBuilder: (context, index) => const SizedBox(height: Sizes.lg), itemBuilder: (context, index) { diff --git a/lib/pages/settings/infos/collaborators_page.dart b/lib/pages/settings/infos/collaborators_page.dart index e7403610..8c9221d8 100644 --- a/lib/pages/settings/infos/collaborators_page.dart +++ b/lib/pages/settings/infos/collaborators_page.dart @@ -1,19 +1,13 @@ -// Settings page. - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../../constants/style.dart'; import '../../../ui/device.dart'; +import '../../../ui/widgets/default_card.dart'; -class CollaboratorsPage extends ConsumerStatefulWidget { - const CollaboratorsPage({super.key}); - - @override - // ignore: library_private_types_in_public_api - ConsumerState createState() => _CollaboratorsPageState(); -} - +// [name, role, url (without scheme)] var collaborators = const [ ["Marco Perugini", "Project Manager", "github.com/theperu"], ["Michele Vulcano", "Maintainer, Backend Dev", "github.com/mikev-cw"], @@ -29,21 +23,30 @@ var collaborators = const [ ["Alessandro Bongiovanni", "Flutter Dev", "github.com/bongio94"], [ "Emanuel Passaro", - "Social Media Manager e Strategist", + "Social Media Manager", "linkedin.com/in/emanuelpassaro/", ], [ "Carolina Verdiani", - "(digital) Marketing Project Manager", + "Marketing Project Manager", "linkedin.com/in/carolina-verdiani/", ], ["Alessia Schina", "UX/UI Designer", "linkedin.com/in/alessiaschina"], ["Federico Bruzzone", "Former Maintainer", "github.com/FedericoBruzzone"], ]; -class _CollaboratorsPageState extends ConsumerState { +// Cycles through the category colour palette for visual variety. +IconData _platformIcon(String url) { + if (url.contains('github.com')) return FontAwesomeIcons.github; + if (url.contains('linkedin.com')) return FontAwesomeIcons.linkedin; + return FontAwesomeIcons.globe; +} + +class CollaboratorsPage extends ConsumerWidget { + const CollaboratorsPage({super.key}); + @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { return Scaffold( appBar: AppBar( leading: IconButton( @@ -52,50 +55,159 @@ class _CollaboratorsPageState extends ConsumerState { ), title: const Text('Collaborators'), ), - body: ListView.separated( - physics: const BouncingScrollPhysics(), - itemCount: collaborators.length, - separatorBuilder: (context, index) => const Divider(height: 1), - itemBuilder: (context, i) { - List option = collaborators[i]; - return InkWell( - onTap: () { - Uri url = Uri.parse("https://${option[2]}"); - launchUrl(url); - }, - child: Padding( - padding: const EdgeInsets.all(Sizes.lg), + body: SingleChildScrollView( + padding: const EdgeInsets.only( + top: Sizes.xl, + bottom: Sizes.xxl, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // ── Header ───────────────────────────────────── + Padding( + padding: const EdgeInsets.symmetric(horizontal: Sizes.lg), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - option[0].toString(), - style: Theme.of(context).textTheme.titleLarge!.copyWith( + 'Meet the team', + style: Theme.of(context).textTheme.headlineMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), - textAlign: TextAlign.left, ), - const SizedBox(height: Sizes.xs), + const SizedBox(height: Sizes.sm), Text( - option[1].toString(), + 'sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.', style: Theme.of(context).textTheme.bodyMedium!.copyWith( - color: Theme.of(context).colorScheme.primary, + color: Theme.of(context).colorScheme.outline, ), - textAlign: TextAlign.left, ), - const SizedBox(height: Sizes.xs), - Text( - option[2].toString(), - style: Theme.of(context).textTheme.bodySmall!.copyWith( - color: Theme.of(context).colorScheme.primary, + ], + ), + ), + const SizedBox(height: Sizes.xl), + + // ── Contributors list ─────────────────────────────────── + Column( + spacing: Sizes.sm, + children: List.generate(collaborators.length, (i) { + final c = collaborators[i]; + return _ContributorCard( + name: c[0], + role: c[1], + url: c[2], + ); + }), + ), + + const SizedBox(height: Sizes.xxl), + + // ── CTA ──────────────────────────────────────────────── + DefaultCard( + onTap: () => launchUrl( + Uri.parse('https://github.com/RIP-Comm/sossoldi'), + ), + child: Row( + spacing: Sizes.md, + children: [ + Container( + decoration: const BoxDecoration( + color: blue5, + shape: BoxShape.circle, + ), + padding: const EdgeInsets.all(Sizes.md), + child: const FaIcon( + FontAwesomeIcons.github, + color: white, + size: 22, + ), + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Want to contribute?', + style: Theme.of( + context, + ).textTheme.titleLarge!.copyWith( + color: Theme.of(context).colorScheme.primary, + ), + ), + Text( + 'Open an issue, submit a PR or just say hi on GitHub', + style: Theme.of( + context, + ).textTheme.bodySmall!.copyWith( + color: Theme.of(context).colorScheme.outline, + ), + ), + ], ), - textAlign: TextAlign.left, + ), + Icon( + Icons.arrow_forward_ios, + size: 16, + color: Theme.of(context).colorScheme.outline, ), ], ), ), - ); - }, + ], + ), + ), + ); + } +} + +class _ContributorCard extends StatelessWidget { + const _ContributorCard({ + required this.name, + required this.role, + required this.url, + }); + + final String name; + final String role; + final String url; + + @override + Widget build(BuildContext context) { + return DefaultCard( + onTap: () => launchUrl(Uri.parse('https://$url')), + child: Row( + spacing: Sizes.md, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + name, + style: Theme.of(context).textTheme.titleSmall!.copyWith( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + Text( + role, + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context).colorScheme.outline, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ), + ), + FaIcon( + _platformIcon(url), + size: 14, + color: Theme.of(context).colorScheme.outline, + ), + ], ), ); } diff --git a/lib/pages/settings/infos/more_info_page.dart b/lib/pages/settings/infos/more_info_page.dart index a6d6bcdb..fb3577eb 100644 --- a/lib/pages/settings/infos/more_info_page.dart +++ b/lib/pages/settings/infos/more_info_page.dart @@ -27,7 +27,8 @@ class MoreInfoPage extends ConsumerWidget { title: const Text('App Info'), ), body: ListView.separated( - padding: const EdgeInsets.symmetric(vertical: Sizes.lg), + padding: const EdgeInsets.only(top: Sizes.xl), + physics: const BouncingScrollPhysics(), itemCount: moreInfoOptions.length, separatorBuilder: (context, index) => const SizedBox(height: Sizes.lg), itemBuilder: (context, index) { diff --git a/lib/pages/settings/notifications/notifications_settings.dart b/lib/pages/settings/notifications/notifications_settings.dart index 61c7a758..3834b1c1 100644 --- a/lib/pages/settings/notifications/notifications_settings.dart +++ b/lib/pages/settings/notifications/notifications_settings.dart @@ -25,38 +25,11 @@ class NotificationsSettings extends ConsumerWidget { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), + title: const Text('Notifications'), ), body: ListView( + padding: const EdgeInsets.only(top: Sizes.xl), children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: Sizes.xl, - horizontal: Sizes.lg, - ), - child: Row( - children: [ - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.primary, - ), - padding: const EdgeInsets.all(Sizes.sm), - child: Icon( - Icons.notifications_active, - size: 24.0, - color: Theme.of(context).colorScheme.onPrimary, - ), - ), - const SizedBox(width: 12.0), - Text( - "Notifications", - style: Theme.of(context).textTheme.headlineLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ), Container( width: double.infinity, margin: const EdgeInsets.symmetric(horizontal: Sizes.lg), diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index c7f1ec9a..f5054b0b 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -6,6 +6,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; + import '../../constants/style.dart'; import '../../ui/widgets/alert_dialog.dart'; import '../../ui/widgets/default_card.dart'; @@ -117,49 +119,16 @@ class _SettingsPageState extends ConsumerState { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), + title: GestureDetector( + onTap: _onSettingsTap, + child: const Text('Settings'), + ), ), - body: SingleChildScrollView( + body: ListView.builder( + padding: const EdgeInsets.only(top: Sizes.xl), physics: const BouncingScrollPhysics(), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric( - vertical: Sizes.xl, - horizontal: Sizes.lg, - ), - child: GestureDetector( - onTap: _onSettingsTap, - child: Row( - children: [ - Container( - decoration: BoxDecoration( - shape: BoxShape.circle, - color: Theme.of(context).colorScheme.primary, - ), - padding: const EdgeInsets.all(Sizes.xs), - child: Icon( - Icons.settings, - size: 28.0, - color: Theme.of(context).colorScheme.surface, - ), - ), - const SizedBox(width: Sizes.md), - Text( - "Settings", - style: Theme.of(context).textTheme.headlineLarge! - .copyWith( - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ), - ), - ListView.builder( - itemCount: settingsOptions.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, i) { + itemCount: settingsOptions.length, + itemBuilder: (context, i) { List setting = settingsOptions[i]; if (setting[3] == null) return Container(); return Padding( @@ -223,9 +192,55 @@ class _SettingsPageState extends ConsumerState { ), ), ); - }, - ), - ], + }, + ), + bottomNavigationBar: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: Sizes.sm), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Open source, built by the community', + style: Theme.of(context).textTheme.bodySmall!.copyWith( + color: Theme.of(context).colorScheme.outline, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: const FaIcon(FontAwesomeIcons.github), + onPressed: () => launchUrl( + Uri.parse('https://github.com/RIP-Comm/sossoldi'), + ), + color: Theme.of(context).colorScheme.primary, + ), + IconButton( + icon: const FaIcon(FontAwesomeIcons.linkedin), + onPressed: () => launchUrl( + Uri.parse('https://www.linkedin.com/company/sossoldi'), + ), + color: Theme.of(context).colorScheme.primary, + ), + IconButton( + icon: const FaIcon(FontAwesomeIcons.youtube), + onPressed: () => launchUrl( + Uri.parse('https://www.youtube.com/@Sossoldi-app'), + ), + color: Theme.of(context).colorScheme.primary, + ), + IconButton( + icon: const FaIcon(FontAwesomeIcons.discord), + onPressed: () => launchUrl( + Uri.parse('https://discord.sossoldi.com'), + ), + color: Theme.of(context).colorScheme.primary, + ), + ], + ), + ], + ), ), ), bottomSheet: _isDeveloperOptionsActive diff --git a/pubspec.lock b/pubspec.lock index f9865848..f5ed85f7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -432,6 +432,14 @@ packages: description: flutter source: sdk version: "0.0.0" + font_awesome_flutter: + dependency: "direct main" + description: + name: font_awesome_flutter + sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0 + url: "https://pub.dev" + source: hosted + version: "10.12.0" freezed: dependency: "direct dev" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6b6c2c02..bf4d8769 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: sqlite3_flutter_libs: timezone: ^0.10.1 url_launcher: ^6.3.2 + font_awesome_flutter: ^10.12.0 dev_dependencies: flutter_test: From 42f486e33ab8f9c2649a9e8cdf9465e314d05b77 Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Thu, 26 Feb 2026 20:00:21 +0100 Subject: [PATCH 08/20] Dart format --- .../settings/infos/collaborators_page.dart | 34 ++---- lib/pages/settings/settings_page.dart | 113 +++++++++--------- 2 files changed, 66 insertions(+), 81 deletions(-) diff --git a/lib/pages/settings/infos/collaborators_page.dart b/lib/pages/settings/infos/collaborators_page.dart index 8c9221d8..564ace8a 100644 --- a/lib/pages/settings/infos/collaborators_page.dart +++ b/lib/pages/settings/infos/collaborators_page.dart @@ -56,10 +56,7 @@ class CollaboratorsPage extends ConsumerWidget { title: const Text('Collaborators'), ), body: SingleChildScrollView( - padding: const EdgeInsets.only( - top: Sizes.xl, - bottom: Sizes.xxl, - ), + padding: const EdgeInsets.only(top: Sizes.xl, bottom: Sizes.xxl), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -92,11 +89,7 @@ class CollaboratorsPage extends ConsumerWidget { spacing: Sizes.sm, children: List.generate(collaborators.length, (i) { final c = collaborators[i]; - return _ContributorCard( - name: c[0], - role: c[1], - url: c[2], - ); + return _ContributorCard(name: c[0], role: c[1], url: c[2]); }), ), @@ -104,9 +97,8 @@ class CollaboratorsPage extends ConsumerWidget { // ── CTA ──────────────────────────────────────────────── DefaultCard( - onTap: () => launchUrl( - Uri.parse('https://github.com/RIP-Comm/sossoldi'), - ), + onTap: () => + launchUrl(Uri.parse('https://github.com/RIP-Comm/sossoldi')), child: Row( spacing: Sizes.md, children: [ @@ -128,19 +120,17 @@ class CollaboratorsPage extends ConsumerWidget { children: [ Text( 'Want to contribute?', - style: Theme.of( - context, - ).textTheme.titleLarge!.copyWith( - color: Theme.of(context).colorScheme.primary, - ), + style: Theme.of(context).textTheme.titleLarge! + .copyWith( + color: Theme.of(context).colorScheme.primary, + ), ), Text( 'Open an issue, submit a PR or just say hi on GitHub', - style: Theme.of( - context, - ).textTheme.bodySmall!.copyWith( - color: Theme.of(context).colorScheme.outline, - ), + style: Theme.of(context).textTheme.bodySmall! + .copyWith( + color: Theme.of(context).colorScheme.outline, + ), ), ], ), diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index f5054b0b..d7cac84e 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -129,69 +129,65 @@ class _SettingsPageState extends ConsumerState { physics: const BouncingScrollPhysics(), itemCount: settingsOptions.length, itemBuilder: (context, i) { - List setting = settingsOptions[i]; - if (setting[3] == null) return Container(); - return Padding( - padding: const EdgeInsets.only(bottom: Sizes.lg), - child: DefaultCard( - onTap: () { - if (setting[3] != null) { - final link = setting[3] as String; - if (link.startsWith("http")) { - Uri url = Uri.parse(link); - launchUrl(url); - } else { - Navigator.of(context).pushNamed(link); - } - } - }, - child: Row( + List setting = settingsOptions[i]; + if (setting[3] == null) return Container(); + return Padding( + padding: const EdgeInsets.only(bottom: Sizes.lg), + child: DefaultCard( + onTap: () { + if (setting[3] != null) { + final link = setting[3] as String; + if (link.startsWith("http")) { + Uri url = Uri.parse(link); + launchUrl(url); + } else { + Navigator.of(context).pushNamed(link); + } + } + }, + child: Row( + children: [ + Container( + decoration: const BoxDecoration( + color: blue5, + shape: BoxShape.circle, + ), + padding: const EdgeInsets.all(Sizes.sm), + child: Icon( + setting[0] as IconData, + size: 30.0, + color: white, + ), + ), + const SizedBox(width: Sizes.md), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Container( - decoration: const BoxDecoration( - color: blue5, - shape: BoxShape.circle, - ), - padding: const EdgeInsets.all(Sizes.sm), - child: Icon( - setting[0] as IconData, - size: 30.0, - color: white, - ), - ), - const SizedBox(width: Sizes.md), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceAround, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - setting[1].toString(), - style: Theme.of(context).textTheme.titleLarge! - .copyWith( - color: Theme.of( - context, - ).colorScheme.primary, - ), + Text( + setting[1].toString(), + style: Theme.of(context).textTheme.titleLarge! + .copyWith( + color: Theme.of(context).colorScheme.primary, ), - Text( - setting[2].toString(), - style: Theme.of(context).textTheme.bodySmall! - .copyWith( - color: Theme.of( - context, - ).colorScheme.primary, - ), - overflow: TextOverflow.ellipsis, - maxLines: 2, + ), + Text( + setting[2].toString(), + style: Theme.of(context).textTheme.bodySmall! + .copyWith( + color: Theme.of(context).colorScheme.primary, ), - ], - ), + overflow: TextOverflow.ellipsis, + maxLines: 2, ), ], ), ), - ); + ], + ), + ), + ); }, ), bottomNavigationBar: SafeArea( @@ -232,9 +228,8 @@ class _SettingsPageState extends ConsumerState { ), IconButton( icon: const FaIcon(FontAwesomeIcons.discord), - onPressed: () => launchUrl( - Uri.parse('https://discord.sossoldi.com'), - ), + onPressed: () => + launchUrl(Uri.parse('https://discord.sossoldi.com')), color: Theme.of(context).colorScheme.primary, ), ], From d2b8577446fc971eafdf65c2ec32cffe363002ab Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Fri, 27 Feb 2026 09:28:21 +0100 Subject: [PATCH 09/20] include recurring transactions in account balance calculations (#517) --- .../database/repositories/account_repository.dart | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/services/database/repositories/account_repository.dart b/lib/services/database/repositories/account_repository.dart index 2bab7486..f75a2387 100644 --- a/lib/services/database/repositories/account_repository.dart +++ b/lib/services/database/repositories/account_repository.dart @@ -75,8 +75,6 @@ class AccountRepository { final db = await _sossoldiDB.database; final where = '${BankAccountFields.active} = 1 '; - final recurringFilter = - '(t.${TransactionFields.recurring} = 0 OR t.${TransactionFields.recurring} IS NULL)'; final result = await db.rawQuery(''' SELECT b.*, (b.${BankAccountFields.startingValue} + @@ -89,7 +87,6 @@ class AccountRepository { LEFT JOIN "$transactionTable" as t ON (t.${TransactionFields.idBankAccount} = b.${BankAccountFields.id} OR t.${TransactionFields.idBankAccountTransfer} = b.${BankAccountFields.id}) - AND $recurringFilter WHERE $where GROUP BY b.${BankAccountFields.id} ORDER BY $orderByASC @@ -266,11 +263,10 @@ class AccountRepository { final accountFilter = "(${TransactionFields.idBankAccount} = $accountId OR ${TransactionFields.idBankAccountTransfer} = $accountId)"; - final recurrentFilter = "(${TransactionFields.recurring} = 0)"; final periodFilterEnd = dateRangeEnd != null ? "strftime('%Y-%m-%d', ${TransactionFields.date}) < '${dateRangeEnd.toString().substring(0, 10)}'" : ""; - final filters = [periodFilterEnd, accountFilter, recurrentFilter]; + final filters = [periodFilterEnd, accountFilter]; final sqlFilters = filters.where((filter) => filter != "").join(" AND "); final resultQuery = await db.rawQuery(''' @@ -322,11 +318,10 @@ class AccountRepository { final accountFilter = "(${TransactionFields.idBankAccount} = $accountId OR ${TransactionFields.idBankAccountTransfer} = $accountId)"; - final recurrentFilter = "(${TransactionFields.recurring} = 0)"; final periodFilterEnd = dateRangeEnd != null ? "strftime('%Y-%m-%d', ${TransactionFields.date}) < '${dateRangeEnd.toString().substring(0, 10)}'" : ""; - final filters = [periodFilterEnd, accountFilter, recurrentFilter]; + final filters = [periodFilterEnd, accountFilter]; final sqlFilters = filters.where((filter) => filter != "").join(" AND "); final resultQuery = await db.rawQuery(''' From 3661dd588965c441930b6e8205c225f3af458bfa Mon Sep 17 00:00:00 2001 From: Luca Antonelli <45290704+lucaantonelli@users.noreply.github.com> Date: Fri, 27 Feb 2026 09:46:22 +0100 Subject: [PATCH 10/20] feat: frequent category list real data (#513) --- .../widgets/category_selector.dart | 5 +++- lib/providers/categories_provider.dart | 14 ++++++++++ .../repositories/category_repository.dart | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/lib/pages/transactions/create_transaction/widgets/category_selector.dart b/lib/pages/transactions/create_transaction/widgets/category_selector.dart index 69fcb8f4..00f928e7 100644 --- a/lib/pages/transactions/create_transaction/widgets/category_selector.dart +++ b/lib/pages/transactions/create_transaction/widgets/category_selector.dart @@ -30,6 +30,9 @@ class _CategorySelectorState extends ConsumerState { final categoriesList = ref.watch( categoriesByTypeProvider(transactionType.categoryType), ); + final frequentCategories = ref.watch( + frequentCategoriesProvider(transactionType.categoryType), + ); final selectedCategory = ref.watch(selectedCategoryProvider); return Container( @@ -71,7 +74,7 @@ class _CategorySelectorState extends ConsumerState { color: Theme.of(context).colorScheme.surface, height: 74, width: double.infinity, - child: categoriesList.when( + child: frequentCategories.when( data: (categories) => ListView.builder( itemCount: categories.length, // to prevent range error scrollDirection: Axis.horizontal, diff --git a/lib/providers/categories_provider.dart b/lib/providers/categories_provider.dart index da456b6b..38faea37 100644 --- a/lib/providers/categories_provider.dart +++ b/lib/providers/categories_provider.dart @@ -208,6 +208,20 @@ Future> subcategories(Ref ref, int categoryId) async { return categories; } +@riverpod +Future> frequentCategories( + Ref ref, + CategoryTransactionType? type, +) async { + List categories = []; + if (type != null) { + categories = await ref + .read(categoryRepositoryProvider) + .selectFrequentCategories(type); + } + return categories; +} + @Riverpod(keepAlive: true) Future> categoryMap(Ref ref) async { final categoryType = ref.watch(categoryTypeProvider); diff --git a/lib/services/database/repositories/category_repository.dart b/lib/services/database/repositories/category_repository.dart index a427be2e..c5654349 100644 --- a/lib/services/database/repositories/category_repository.dart +++ b/lib/services/database/repositories/category_repository.dart @@ -1,6 +1,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import '../../../model/category_transaction.dart'; +import '../../../model/transaction.dart'; import '../sossoldi_database.dart'; part 'category_repository.g.dart'; @@ -112,6 +113,33 @@ class CategoryRepository { } } + Future> selectFrequentCategories( + CategoryTransactionType type, + ) async { + final db = await _sossoldiDB.database; + // Select the last 100 transactions, group by category and return the + // top 5 most used categories ordered by usage count desc. + final result = await db.rawQuery( + ''' + SELECT c.* + FROM "$categoryTransactionTable" c + JOIN ( + SELECT * FROM "$transactionTable" + WHERE "${TransactionFields.type}" = ? + ORDER BY "${TransactionFields.date}" DESC + LIMIT 100 + ) t ON t."${TransactionFields.idCategory}" = c."${CategoryTransactionFields.id}" + WHERE c."${CategoryTransactionFields.type}" = ? + GROUP BY c."${CategoryTransactionFields.id}" + ORDER BY COUNT(t."${TransactionFields.id}") DESC + LIMIT 5 + ''', + [type.transactionType.code, type.code], + ); + + return result.map((json) => CategoryTransaction.fromJson(json)).toList(); + } + Future updateItem(CategoryTransaction item) async { final db = await _sossoldiDB.database; From 127a46cf9852b5f64f007323809a7cf80be215fb Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Fri, 27 Feb 2026 14:34:06 +0100 Subject: [PATCH 11/20] Fix: demo data issues and category reorder with subcategories (#515) * Fix demo data * Dart format * Fix category reordering when subcategories are added --- lib/providers/categories_provider.dart | 11 ++++--- lib/services/database/sossoldi_database.dart | 34 ++++++++++---------- 2 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/providers/categories_provider.dart b/lib/providers/categories_provider.dart index 38faea37..85357d7a 100644 --- a/lib/providers/categories_provider.dart +++ b/lib/providers/categories_provider.dart @@ -157,14 +157,15 @@ class Categories extends _$Categories { newIndex -= 1; } - final newList = List.from(currentList); - final item = newList.removeAt(oldIndex); - newList.insert(newIndex, item); + final parents = currentList.where((c) => c.parent == null).toList(); + final item = parents.removeAt(oldIndex); + parents.insert(newIndex, item); - state = AsyncData(newList); + final subcategories = currentList.where((c) => c.parent != null).toList(); + state = AsyncData([...parents, ...subcategories]); await AsyncValue.guard(() async { - await ref.read(categoryRepositoryProvider).updateOrders(newList); + await ref.read(categoryRepositoryProvider).updateOrders(parents); }); } } diff --git a/lib/services/database/sossoldi_database.dart b/lib/services/database/sossoldi_database.dart index 163e8984..f76e5c18 100644 --- a/lib/services/database/sossoldi_database.dart +++ b/lib/services/database/sossoldi_database.dart @@ -208,24 +208,24 @@ class SossoldiDatabase { } Future fillDemoData({int countOfGeneratedTransaction = 10000}) async { - // Add some fake accounts + // Add fake accounts await _database?.execute(''' - INSERT INTO bankAccount(id, name, symbol, color, startingValue, active, mainAccount, createdAt, updatedAt) VALUES - (70, 'Revolut', 'payments', 1, 1235.10, 1, 1, '${DateTime.now()}', '${DateTime.now()}'), - (71, 'N26', 'credit_card', 2, 3823.56, 1, 0, '${DateTime.now()}', '${DateTime.now()}'), - (72, 'Fineco', 'account_balance', 3, 0.00, 1, 0, '${DateTime.now()}', '${DateTime.now()}'); + INSERT INTO bankAccount(id, name, symbol, color, startingValue, active, countNetWorth, mainAccount, position, createdAt, updatedAt) VALUES + (70, 'Revolut', 'payments', 1, 1235.10, 1, 1, 1, 0, '${DateTime.now()}', '${DateTime.now()}'), + (71, 'N26', 'credit_card', 2, 3823.56, 1, 1, 0, 1, '${DateTime.now()}', '${DateTime.now()}'), + (72, 'Fineco', 'account_balance', 3, 0.00, 1, 1, 0, 2, '${DateTime.now()}', '${DateTime.now()}'); '''); - // Add fake categories + // Add fake categories and subcategories await _database?.execute(''' - INSERT INTO categoryTransaction(id, name, type, symbol, color, note, parent, createdAt, updatedAt) VALUES - (10, 'Out', 'OUT', 'restaurant', 0, '', null, '${DateTime.now()}', '${DateTime.now()}'), - (11, 'Home', 'OUT', 'home', 1, '', null, '${DateTime.now()}', '${DateTime.now()}'), - (12, 'Furniture','OUT', 'home', 2, '', 11, '${DateTime.now()}', '${DateTime.now()}'), - (13, 'Shopping', 'OUT', 'shopping_cart', 3, '', null, '${DateTime.now()}', '${DateTime.now()}'), - (14, 'Leisure', 'OUT', 'subscriptions', 4, '', null, '${DateTime.now()}', '${DateTime.now()}'), - (15, 'Transports', 'OUT', 'directions_car', 6, '', null, '${DateTime.now()}', '${DateTime.now()}'), - (16, 'Salary', 'IN', 'work', 5, '', null, '${DateTime.now()}', '${DateTime.now()}'); + INSERT INTO categoryTransaction(id, name, type, symbol, color, note, parent, position, createdAt, updatedAt) VALUES + (10, 'Out', 'OUT', 'restaurant', 0, '', null, 0, '${DateTime.now()}', '${DateTime.now()}'), + (11, 'Home', 'OUT', 'home', 1, '', null, 1, '${DateTime.now()}', '${DateTime.now()}'), + (12, 'Furniture','OUT', 'home', 1, '', 11, 2, '${DateTime.now()}', '${DateTime.now()}'), + (13, 'Shopping', 'OUT', 'shopping_cart', 3, '', null, 3, '${DateTime.now()}', '${DateTime.now()}'), + (14, 'Leisure', 'OUT', 'subscriptions', 4, '', null, 4, '${DateTime.now()}', '${DateTime.now()}'), + (15, 'Transports', 'OUT', 'directions_car', 6, '', null, 5, '${DateTime.now()}', '${DateTime.now()}'), + (16, 'Salary', 'IN', 'work', 5, '', null, 6, '${DateTime.now()}', '${DateTime.now()}'); '''); // Add currencies @@ -246,7 +246,7 @@ class SossoldiDatabase { // Add fake recurring transactions await _database?.execute(''' - INSERT INTO recurringTransaction(fromDate, toDate, amount,type, note, recurrency, idCategory, idBankAccount, createdAt, updatedAt) VALUES + INSERT INTO recurringTransaction(fromDate, toDate, amount, type, note, recurrency, idCategory, idBankAccount, createdAt, updatedAt) VALUES ('2024-02-23', null, 10.99, 'OUT', '404 Books', 'MONTHLY', 14, 70, '${DateTime.now()}', '${DateTime.now()}'), ('2023-12-13', null, 4.97, 'OUT', 'ETF Consultant Parcel', 'DAILY', 14, 70, '${DateTime.now()}', '${DateTime.now()}'), ('2023-02-11', '2028-02-11', 1193.40, 'OUT', 'Car Loan', 'QUARTERLY', 15, 72, '${DateTime.now()}', '${DateTime.now()}'); @@ -308,7 +308,7 @@ class SossoldiDatabase { var randomType = 'OUT'; var randomAccount = accounts[rnd.nextInt(accounts.length)]; var randomNote = outNotes[rnd.nextInt(outNotes.length)]; - var randomCategory = categories[rnd.nextInt(categories.length)]; + int? randomCategory = categories[rnd.nextInt(categories.length)]; int? idBankAccountTransfer; DateTime randomDate = now.subtract( Duration( @@ -324,7 +324,7 @@ class SossoldiDatabase { randomNote = 'Transfer'; randomAccount = 70; // sender account is hardcoded with the one that receives our fake salary - randomCategory = 0; // no category for transfers + randomCategory = null; // transfers have no category idBankAccountTransfer = accounts[rnd.nextInt(accounts.length)]; randomAmount = (fakeSalary / 100) * 70; From 0c36fbd423c5b220ebc7ac2fb1d689a3caa15375 Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Fri, 27 Feb 2026 19:19:23 +0100 Subject: [PATCH 12/20] Moving urls to constants.dart --- lib/constants/constants.dart | 6 ++++++ lib/pages/settings/infos/collaborators_page.dart | 4 ++-- lib/pages/settings/settings_page.dart | 16 +++++----------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/constants/constants.dart b/lib/constants/constants.dart index a5245c16..28e9b477 100644 --- a/lib/constants/constants.dart +++ b/lib/constants/constants.dart @@ -297,6 +297,12 @@ const darkAccountColorList = [ List categoryColorListTheme = categoryColorList; List accountColorListTheme = accountColorList; +// external URLs +const String githubUrl = 'https://github.com/RIP-Comm/sossoldi'; +const String linkedinUrl = 'https://www.linkedin.com/company/sossoldi'; +const String youtubeUrl = 'https://www.youtube.com/@Sossoldi-app'; +const String discordUrl = 'https://discord.sossoldi.com'; + void updateColorsBasedOnTheme(bool isDarkModeEnabled) { if (isDarkModeEnabled) { categoryColorListTheme = darkCategoryColorList; diff --git a/lib/pages/settings/infos/collaborators_page.dart b/lib/pages/settings/infos/collaborators_page.dart index 564ace8a..074b47cf 100644 --- a/lib/pages/settings/infos/collaborators_page.dart +++ b/lib/pages/settings/infos/collaborators_page.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../../constants/constants.dart'; import '../../../constants/style.dart'; import '../../../ui/device.dart'; import '../../../ui/widgets/default_card.dart'; @@ -97,8 +98,7 @@ class CollaboratorsPage extends ConsumerWidget { // ── CTA ──────────────────────────────────────────────── DefaultCard( - onTap: () => - launchUrl(Uri.parse('https://github.com/RIP-Comm/sossoldi')), + onTap: () => launchUrl(Uri.parse(githubUrl)), child: Row( spacing: Sizes.md, children: [ diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index d7cac84e..b0a0fb74 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -8,6 +8,7 @@ import 'package:url_launcher/url_launcher.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import '../../constants/constants.dart'; import '../../constants/style.dart'; import '../../ui/widgets/alert_dialog.dart'; import '../../ui/widgets/default_card.dart'; @@ -207,29 +208,22 @@ class _SettingsPageState extends ConsumerState { children: [ IconButton( icon: const FaIcon(FontAwesomeIcons.github), - onPressed: () => launchUrl( - Uri.parse('https://github.com/RIP-Comm/sossoldi'), - ), + onPressed: () => launchUrl(Uri.parse(githubUrl)), color: Theme.of(context).colorScheme.primary, ), IconButton( icon: const FaIcon(FontAwesomeIcons.linkedin), - onPressed: () => launchUrl( - Uri.parse('https://www.linkedin.com/company/sossoldi'), - ), + onPressed: () => launchUrl(Uri.parse(linkedinUrl)), color: Theme.of(context).colorScheme.primary, ), IconButton( icon: const FaIcon(FontAwesomeIcons.youtube), - onPressed: () => launchUrl( - Uri.parse('https://www.youtube.com/@Sossoldi-app'), - ), + onPressed: () => launchUrl(Uri.parse(youtubeUrl)), color: Theme.of(context).colorScheme.primary, ), IconButton( icon: const FaIcon(FontAwesomeIcons.discord), - onPressed: () => - launchUrl(Uri.parse('https://discord.sossoldi.com')), + onPressed: () => launchUrl(Uri.parse(discordUrl)), color: Theme.of(context).colorScheme.primary, ), ], From 650062c627965399aa8f31c4bb9034bea1043072 Mon Sep 17 00:00:00 2001 From: Marco Perugini Date: Sat, 28 Feb 2026 12:02:39 +0100 Subject: [PATCH 13/20] fix: sync subcategory colors when parent category color is updated --- lib/providers/categories_provider.dart | 4 ++++ .../database/repositories/category_repository.dart | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/providers/categories_provider.dart b/lib/providers/categories_provider.dart index da456b6b..371af936 100644 --- a/lib/providers/categories_provider.dart +++ b/lib/providers/categories_provider.dart @@ -106,8 +106,12 @@ class Categories extends _$Categories { state = const AsyncLoading(); state = await AsyncValue.guard(() async { await ref.read(categoryRepositoryProvider).updateItem(category); + await ref + .read(categoryRepositoryProvider) + .updateSubcategoriesColor(category.id!, color); ref.invalidate(selectedCategoryProvider); ref.invalidate(categoryMapProvider); + ref.invalidate(subcategoriesProvider(category.id!)); return _getCategories(); }); } diff --git a/lib/services/database/repositories/category_repository.dart b/lib/services/database/repositories/category_repository.dart index a427be2e..0a5d9031 100644 --- a/lib/services/database/repositories/category_repository.dart +++ b/lib/services/database/repositories/category_repository.dart @@ -124,6 +124,17 @@ class CategoryRepository { ); } + Future updateSubcategoriesColor(int parentId, int color) async { + final db = await _sossoldiDB.database; + + await db.update( + categoryTransactionTable, + {CategoryTransactionFields.color: color}, + where: '${CategoryTransactionFields.parent} = ?', + whereArgs: [parentId], + ); + } + Future deleteById(int id) async { final db = await _sossoldiDB.database; From 813145f62f1073e225c380328cc1d896f18c4ca3 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Thu, 5 Mar 2026 14:34:16 +0100 Subject: [PATCH 14/20] Implemented import from MoneyManager --- lib/pages/settings/backup/backup_page.dart | 153 ++++--- .../settings/infos/collaborators_page.dart | 1 + lib/services/database/sossoldi_database.dart | 394 ++++++++++++++++++ 3 files changed, 497 insertions(+), 51 deletions(-) diff --git a/lib/pages/settings/backup/backup_page.dart b/lib/pages/settings/backup/backup_page.dart index c38b23e2..ce50fee0 100644 --- a/lib/pages/settings/backup/backup_page.dart +++ b/lib/pages/settings/backup/backup_page.dart @@ -1,3 +1,5 @@ + + import 'package:flutter/material.dart'; import 'package:flutter_phoenix/flutter_phoenix.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -7,6 +9,7 @@ import '../../../services/csv/csv_file_picker.dart'; import '../../../ui/snack_bars/snack_bar.dart'; import '../../../ui/widgets/default_card.dart'; + class BackupPage extends ConsumerStatefulWidget { const BackupPage({super.key}); @@ -42,39 +45,69 @@ class BackupOption { } } +enum CsvSource{ + sossoldi, + moneyManager +} + class _BackupPageState extends ConsumerState { - Future _handleImport() async { + + Future _handleImport({required CsvSource source}) async { try { final file = await CSVFilePicker.pickCSVFile(context); - if (file != null) { - if (!mounted) return; - CSVFilePicker.showLoading(context, 'Importing data...'); - final results = await SossoldiDatabase.instance.importFromCSV( - file.path, - ); - if (!mounted) return; - CSVFilePicker.hideLoading(context); - - if (results.values.every((success) => success)) { - await CSVFilePicker.showSuccess( - context, - 'Data imported successfully', + if (file == null) { + return; + } + + if (!mounted) return; + + CSVFilePicker.showLoading(context, 'Importing data...'); + + switch(source) + { + case CsvSource.sossoldi: + final results = await SossoldiDatabase.instance.importFromCSV( + file.path, ); - if (mounted) Phoenix.rebirth(context); - } else { - final failedTables = results.entries - .where((e) => !e.value) - .map((e) => e.key) - .join(', '); if (!mounted) return; + CSVFilePicker.hideLoading(context); + + if (results.values.every((success) => success)) { + await CSVFilePicker.showSuccess( + context, + 'Data imported successfully', + ); + if (mounted) Phoenix.rebirth(context); + } else { + final failedTables = results.entries + .where((e) => !e.value) + .map((e) => e.key) + .join(', '); + + throw Exception('Failed to import some tables: $failedTables'); + } + break; + + case CsvSource.moneyManager: + final result = await SossoldiDatabase.instance.importFromCsvFromMoneyManager( + file.path, + ); - showSnackBar( + if(!result) { + throw Exception('Failed to import data from CSV'); + } + + await CSVFilePicker.showSuccess( context, - message: 'Failed to import some tables: $failedTables', + 'Data imported successfully', ); - } + if (mounted) Phoenix.rebirth(context); + + break; } + + } catch (e) { if (!mounted) return; CSVFilePicker.hideLoading(context); @@ -106,6 +139,11 @@ class _BackupPageState extends ConsumerState { description: 'Import a CSV file to update your database', icon: Icons.upload_file, ), + BackupOption( + title: 'Import data', + description: 'Import from CSV from Money Manager\n to update your database\nThe file must be resaved in csv from xls', + icon: Icons.upload_file, + ), BackupOption( title: 'Export data', description: 'Save your data as a CSV file', @@ -118,6 +156,34 @@ class _BackupPageState extends ConsumerState { super.initState(); } + void showImportAlert(context, function) + { + showDialog( + context: context, + builder: (context) => AlertDialog( + title: const Text('Warning: Data Overwrite'), + content: const Text( + 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(); + function(); + }, + child: const Text('Proceed with Import'), + ), + ], + ), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -144,34 +210,19 @@ class _BackupPageState extends ConsumerState { final option = options[index]; return DefaultCard( onTap: () { - if (index == 0) { - // Show confirmation dialog for the first option - showDialog( - context: context, - builder: (context) => AlertDialog( - title: const Text('Warning: Data Overwrite'), - content: const Text( - 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - _handleImport(); - }, - child: const Text('Proceed with Import'), - ), - ], - ), - ); - } else { - _handleExport(); + switch(index) + { + case 0: + showImportAlert(context, () => _handleImport(source: CsvSource.sossoldi)); + break; + case 1: + showImportAlert(context, () => _handleImport(source: CsvSource.moneyManager)); + break; + case 2: + _handleExport(); + break; + default: + throw UnimplementedError(); } }, child: Row( diff --git a/lib/pages/settings/infos/collaborators_page.dart b/lib/pages/settings/infos/collaborators_page.dart index e7403610..fd4c3043 100644 --- a/lib/pages/settings/infos/collaborators_page.dart +++ b/lib/pages/settings/infos/collaborators_page.dart @@ -39,6 +39,7 @@ var collaborators = const [ ], ["Alessia Schina", "UX/UI Designer", "linkedin.com/in/alessiaschina"], ["Federico Bruzzone", "Former Maintainer", "github.com/FedericoBruzzone"], + ["Mattia Sacchi", "Software Developer", "github.com/Mattia-Sacchi"], ]; class _CollaboratorsPageState extends ConsumerState { diff --git a/lib/services/database/sossoldi_database.dart b/lib/services/database/sossoldi_database.dart index f76e5c18..c597c640 100644 --- a/lib/services/database/sossoldi_database.dart +++ b/lib/services/database/sossoldi_database.dart @@ -3,6 +3,7 @@ import 'dart:math'; // used for random number generation in demo data import 'dart:developer' as dev; import 'package:csv/csv.dart'; +import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:path/path.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; @@ -19,6 +20,63 @@ import 'migration_manager.dart'; part 'sossoldi_database.g.dart'; + + +TransactionType? translateTransactionType(String tt) +{ + switch(tt) + { + case 'Income': + return TransactionType.income; + case 'Expense': + return TransactionType.expense; + case 'Transfer-Out': + return TransactionType.transfer; + default: + return null; + } +} + +class MoneyManagerTransaction { + final DateTime date; + final String account; + // In case of transfer + final String? destinationAccount; + // Could be also an account fi the type is transaction + final String category; + final String? subCategory; + String? description; + // Still Don't know the difference between description and note + String? note; + String currency; + final double amount; + + final TransactionType type; // Income o Expense or transaction + + MoneyManagerTransaction({required this.date, required this.account, required this.category, required this.amount, required this.type,required this.currency, this.destinationAccount, this.subCategory, this.description, this.note}); + factory MoneyManagerTransaction.transfer({required DateTime date, required String account, required String dAccount, required amount,required currency, description,note}) + { + return MoneyManagerTransaction(date: date, account: account, category: '', amount: amount, type: TransactionType.transfer,currency: currency, destinationAccount : dAccount, description: description, note: note ); + } + + factory MoneyManagerTransaction.income({required DateTime date, required String account, required category, required amount,required currency,sub,description,note}) + { + return MoneyManagerTransaction(date: date, account: account, category: category, amount: amount, type: TransactionType.income,currency: currency ,subCategory: sub, description: description, note: note ); + } + + factory MoneyManagerTransaction.expense({required DateTime date, required String account, required category, required amount,required currency,sub,description,note}) + { + return MoneyManagerTransaction(date: date, account: account, category: category, amount: amount, type: TransactionType.expense,currency: currency, subCategory: sub , description: description, note: note ); + } + + factory MoneyManagerTransaction.invalid() + { + return MoneyManagerTransaction(date: DateTime.now(), account: 'Invalid', category: 'Invalid', amount: 0.0, type: TransactionType.expense,currency: 'ZWD'); + } + + +} + @Riverpod(keepAlive: true) SossoldiDatabase database(Ref ref) => SossoldiDatabase.instance; @@ -207,6 +265,342 @@ class SossoldiDatabase { return results; } + String sanitizeAlphaNumeric(String text) { + return text + .replaceAll(RegExp(r'[^\p{L}\p{N}\s]+', unicode: true), '') + .replaceAll(RegExp(r'\s+'), ' ') + .trim(); + } + + Future getBankAccountId({required txn, required String name}) async { + final List> maps = await txn.rawQuery( + 'SELECT id FROM bankAccount WHERE name = ? LIMIT 1', + [name], + ); + + if (maps.isEmpty) { + return null; + } + + return maps.first['id'] as int; + } + + Future getCategoryId( {required txn,required String name,required String type}) async { + String query = 'SELECT id FROM categoryTransaction WHERE name = ?'; + List args = [name]; + + query += ' AND type = ?'; + args.add(type); + + + query += ' LIMIT 1'; + + final List> maps = await txn.rawQuery(query, args); + + if (maps.isEmpty) { + return null; + } + + return maps.first['id'] as int; + } + + Future insertTransactionFromMoneyManager( {required txn,required MoneyManagerTransaction transaction}) async { + var backAccountId = await getBankAccountId(txn: txn, name: transaction.account); + Map row = {}; + String currentDate = transaction.date.toIso8601String(); + String code = transaction.type.code; + + if(backAccountId == null) + { + throw Exception('Error during inserting ${transaction.date} transaction'); + } + + switch(transaction.type) + { + case TransactionType.income: + case TransactionType.expense: + var categoryId = await getCategoryId(txn: txn, type: code, name: transaction.category); + if(categoryId == null) + { + throw Exception('Error during inserting ${transaction.date} transaction'); + } + row = { + TransactionFields.date: currentDate, + TransactionFields.amount: transaction.amount, + TransactionFields.type: code, + TransactionFields.note: transaction.description, + TransactionFields.idCategory: categoryId, + TransactionFields.idBankAccount: backAccountId, + TransactionFields.recurring: 0, + TransactionFields.idRecurringTransaction: null, + TransactionFields.createdAt: currentDate, + TransactionFields.updatedAt: DateTime.now().toIso8601String(), + }; + break; + case TransactionType.transfer: + var backAccountReceiverId = await getBankAccountId(txn: txn, name: transaction.destinationAccount!); + if(backAccountReceiverId == null) + { + throw Exception('Error during inserting ${transaction.date} transaction'); + } + row = { + TransactionFields.date : currentDate, + TransactionFields.amount : transaction.amount, + TransactionFields.type: code, + TransactionFields.note: transaction.description, + TransactionFields.idBankAccount: backAccountId, + TransactionFields.idBankAccountTransfer : backAccountReceiverId, + TransactionFields.recurring: 0, + TransactionFields.idRecurringTransaction: null, + TransactionFields.createdAt: currentDate, + TransactionFields.updatedAt: DateTime.now().toIso8601String(), + }; + break; + } + txn.insert('transaction', row); + } + + Future insertCategoriesFromMoneyManager({required txn, required Map> categoryMap, required String code, required DateTime oldestDate}) async + { + List> maps = await txn.query( + categoryTransactionTable, + columns: [CategoryTransactionFields.name], + where: '${CategoryTransactionFields.type} = ?', + whereArgs: [code], + ); + + List currentCategories = maps.map((row) => row[CategoryTransactionFields.name] as String).toList(); + + for(var c in categoryMap.keys) + { + if(!currentCategories.contains(c)) + { + Map row = { + CategoryTransactionFields.name: c, + CategoryTransactionFields.type: code, + CategoryTransactionFields.symbol: 1, + CategoryTransactionFields.color: 0, + CategoryTransactionFields.createdAt: oldestDate.toIso8601String(), + CategoryTransactionFields.updatedAt: oldestDate.toIso8601String(), + }; + txn.insert(categoryTransactionTable, row); + } + + List subs = categoryMap[c]!; + + if(subs.isEmpty) { + continue; + } + + var categoryId = await getCategoryId(txn: txn, name: c, type: code); + + if(categoryId == null) + { + continue; + } + + for(var subCategory in subs) + { + Map row = { + CategoryTransactionFields.name: subCategory, + CategoryTransactionFields.type: code, + CategoryTransactionFields.symbol: 1, + CategoryTransactionFields.color: 0, + CategoryTransactionFields.parent : categoryId, + CategoryTransactionFields.createdAt: oldestDate.toIso8601String(), + CategoryTransactionFields.updatedAt: oldestDate.toIso8601String(), + }; + + txn.insert(categoryTransactionTable, row); + } + } + } + + // I consider being called in a try catch + Future importFromCsvFromMoneyManager(String csvFilePath) async { + + await clearDatabase(); + + final db = await database; + + try { + final file = File(csvFilePath); + + if (!await file.exists()) { + throw Exception('CSV file not found'); + } + + final String csvData = await file.readAsString(); + final List> rows = const CsvToListConverter(eol: '\n', shouldParseNumbers: false).convert( + csvData, + ); + + if (rows.isEmpty) { + throw Exception('CSV file is empty'); + } + + // First row contains headers + final List headers = rows.first.map((e) => e.toString()).toList(); + + const List expectedHeaders = [ + 'Date', + 'Account', + 'Category', + 'Subcategory', + 'Note', + // Still need to check how this column works + // I think it is the default chosen currency in money manager + // 'EUR', + 'Income/Expense', + 'Description', + 'Amount', + 'Currency', + 'Account' + ]; + + for (var str in expectedHeaders) { + if(!headers.contains(str)) { + throw Exception('Column $str not found in CSV file'); + } + } + + // We can discard the headers + rows.removeAt(0); + + List transactions = []; + Set accounts = {}; + Map> expenseCategories = {}; + Map> incomeCategories = {}; + Set currencies = {}; + DateFormat format = DateFormat("MM/dd/yyyy HH:mm:ss"); + DateTime oldest = DateTime.now(); + + // First elaboration + for (var row in rows) { + DateTime dateTime = format.parse(row[0]); + + if(dateTime.isBefore(oldest)) + { + oldest = dateTime; + } + TransactionType? tt = translateTransactionType(row[6]); + String account =sanitizeAlphaNumeric(row[1]); + String cat = sanitizeAlphaNumeric(row[2]); + String sub = sanitizeAlphaNumeric(row[3]); + double money = double.parse(row[8]); + + switch(tt) { + case TransactionType.expense: + if (!expenseCategories.containsKey(cat)) { + expenseCategories[cat] = []; + } + + if (sub.isNotEmpty) { + expenseCategories[cat]!.add(sub); + } + + transactions.add(MoneyManagerTransaction.expense(date: dateTime, account: account, category: cat, amount: money, currency: row[9],sub: sub,note: row[4], description: row[7])); + break; + + case TransactionType.income: + if (!incomeCategories.containsKey(cat)) { + incomeCategories[cat] = []; + } + + if (sub.isNotEmpty) { + incomeCategories[cat]!.add(sub); + } + + transactions.add(MoneyManagerTransaction.income(date: dateTime, account: account, category: cat, amount: money, currency: row[9],sub: sub,note: row[4], description: row[7])); + break; + + case TransactionType.transfer: + // It is normal in this case the category is the destination account + + transactions.add(MoneyManagerTransaction.transfer(date: dateTime, account: account, dAccount: cat, amount: money, currency: row[9],note: row[4], description: row[7])); + accounts.add(cat); + break; + default: + throw Exception('Income/Expenses column found ${row[6]}, undefined behaviour', ); + + } + currencies.add(row[9]); + accounts.add(account); + } + + List> maps = await db.query( + bankAccountTable, + columns: [BankAccountFields.name], + ); + + List currentAccounts = List.generate(maps.length, (i) { + return maps[i][BankAccountFields.name] as String; + }); + + maps = await db.query( + currencyTable, + columns: [CurrencyFields.code], + ); + + List currentCurrencies = maps.map((row) => row[CurrencyFields.code] as String).toList(); + + await db.transaction((txn) async { + for(var a in accounts) + { + if(!currentAccounts.contains(a)) + { + Map row = { + BankAccountFields.name : a, + BankAccountFields.symbol : '1', + BankAccountFields.color : 0, + BankAccountFields.startingValue : 0.0, + BankAccountFields.active : 1, + BankAccountFields.countNetWorth : 1, + BankAccountFields.mainAccount : 0, + BankAccountFields.createdAt : oldest.toIso8601String(), + BankAccountFields.updatedAt : oldest.toIso8601String(), + }; + txn.insert(bankAccountTable, row); + } + } + + insertCategoriesFromMoneyManager(txn: txn, categoryMap: incomeCategories, code: 'IN', oldestDate: oldest); + insertCategoriesFromMoneyManager(txn: txn, categoryMap: expenseCategories, code: 'OUT', oldestDate: oldest); + + + for(var c in currencies) { + if (!currentCurrencies.contains(c)) { + Map row = { + + CurrencyFields.symbol: c, + CurrencyFields.code: c, + CurrencyFields.name: c, + CurrencyFields.mainCurrency: 0, + }; + + txn.insert( + currencyTable, + row, + conflictAlgorithm: ConflictAlgorithm.ignore, + ); + } + } + + + for(var transaction in transactions) + { + await insertTransactionFromMoneyManager(txn: txn, transaction: transaction); + } + }); + + return true; + + } catch (e) { + dev.log('Error during import: $e'); + rethrow; + } + } + Future fillDemoData({int countOfGeneratedTransaction = 10000}) async { // Add fake accounts await _database?.execute(''' From dd70d4a61afaaf5bc0b8cc9534148a834f67a3f1 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Fri, 6 Mar 2026 22:13:01 +0100 Subject: [PATCH 15/20] Bug fixes, The icons and colors are now randomized on import (MoneyManager does not provide them) --- lib/services/database/sossoldi_database.dart | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/services/database/sossoldi_database.dart b/lib/services/database/sossoldi_database.dart index c597c640..2e4a3e82 100644 --- a/lib/services/database/sossoldi_database.dart +++ b/lib/services/database/sossoldi_database.dart @@ -10,6 +10,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:sqflite/sqflite.dart'; // Models +import '../../constants/constants.dart'; import '../../model/bank_account.dart'; import '../../model/budget.dart'; import '../../model/category_transaction.dart'; @@ -359,7 +360,6 @@ class SossoldiDatabase { } txn.insert('transaction', row); } - Future insertCategoriesFromMoneyManager({required txn, required Map> categoryMap, required String code, required DateTime oldestDate}) async { List> maps = await txn.query( @@ -369,17 +369,18 @@ class SossoldiDatabase { whereArgs: [code], ); - List currentCategories = maps.map((row) => row[CategoryTransactionFields.name] as String).toList(); + List currentCategories = maps.map((row) => row[CategoryTransactionFields.name] as String).toList(); for(var c in categoryMap.keys) { + int randomColor = Random().nextInt(categoryColorList.length); if(!currentCategories.contains(c)) { Map row = { CategoryTransactionFields.name: c, CategoryTransactionFields.type: code, - CategoryTransactionFields.symbol: 1, - CategoryTransactionFields.color: 0, + CategoryTransactionFields.symbol: householdIconList.keys.elementAt(Random().nextInt(householdIconList.length)), + CategoryTransactionFields.color: randomColor, CategoryTransactionFields.createdAt: oldestDate.toIso8601String(), CategoryTransactionFields.updatedAt: oldestDate.toIso8601String(), }; @@ -404,8 +405,8 @@ class SossoldiDatabase { Map row = { CategoryTransactionFields.name: subCategory, CategoryTransactionFields.type: code, - CategoryTransactionFields.symbol: 1, - CategoryTransactionFields.color: 0, + CategoryTransactionFields.symbol: activitiesIconList.keys.elementAt(Random().nextInt(activitiesIconList.length)), + CategoryTransactionFields.color: randomColor, CategoryTransactionFields.parent : categoryId, CategoryTransactionFields.createdAt: oldestDate.toIso8601String(), CategoryTransactionFields.updatedAt: oldestDate.toIso8601String(), @@ -419,7 +420,7 @@ class SossoldiDatabase { // I consider being called in a try catch Future importFromCsvFromMoneyManager(String csvFilePath) async { - await clearDatabase(); + await resetDatabase(); final db = await database; @@ -551,8 +552,8 @@ class SossoldiDatabase { { Map row = { BankAccountFields.name : a, - BankAccountFields.symbol : '1', - BankAccountFields.color : 0, + BankAccountFields.symbol : accountIconList.keys.elementAt(Random().nextInt(accountIconList.length)), + BankAccountFields.color : Random().nextInt(accountColorList.length), BankAccountFields.startingValue : 0.0, BankAccountFields.active : 1, BankAccountFields.countNetWorth : 1, From 73863f10c1bf4fe8b01dde94ce0f947259b9a944 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Sat, 7 Mar 2026 19:17:56 +0100 Subject: [PATCH 16/20] Start to work on translations --- l10n.yaml | 3 + lib/constants/constants.dart | 2 + lib/constants/exceptions.dart | 64 + lib/l10n/app_en.arb | 254 +++ lib/l10n/app_it.arb | 253 +++ lib/l10n/app_localizations.dart | 1536 +++++++++++++++++ lib/l10n/app_localizations_en.dart | 777 +++++++++ lib/l10n/app_localizations_it.dart | 783 +++++++++ lib/l10n/app_localizations_pt.dart | 782 +++++++++ lib/l10n/app_pt.arb | 240 +++ lib/main.dart | 21 + lib/pages/accounts/account_list_page.dart | 9 +- lib/pages/accounts/account_page.dart | 24 +- .../accounts/create_edit_account_page.dart | 28 +- lib/pages/categories/category_list_page.dart | 7 +- .../categories/create_edit_category_page.dart | 18 +- .../create_edit_subcategory_page.dart | 14 +- .../widgets/category_icon_color_selector.dart | 11 +- .../widgets/subcategories_list.dart | 5 +- lib/pages/dashboard/dashboard_page.dart | 20 +- .../dashboard/widgets/account_section.dart | 6 +- lib/pages/dashboard/widgets/accounts_sum.dart | 1 + .../dashboard/widgets/budgets_section.dart | 10 +- lib/pages/graphs/graphs_page.dart | 9 +- .../widgets/accounts/accounts_card.dart | 7 +- .../widgets/categories/categories_card.dart | 10 +- .../categories_graph_pie_chart.dart | 3 +- lib/pages/onboarding/onboarding_page.dart | 8 +- .../onboarding/widgets/account_setup.dart | 27 +- .../onboarding/widgets/add_budget_dialog.dart | 8 +- .../widgets/add_category_button.dart | 3 +- .../onboarding/widgets/budget_setup.dart | 16 +- .../onboarding/widgets/category_button.dart | 6 +- lib/pages/planning/manage_budget_page.dart | 38 +- lib/pages/planning/planning_page.dart | 8 +- lib/pages/planning/widget/budget_card.dart | 16 +- .../widget/budget_category_selector.dart | 2 + .../planning/widget/budget_pie_chart.dart | 6 +- .../widget/edit_recurring_transaction.dart | 2 + .../widget/older_recurring_payments.dart | 20 +- .../widget/recurring_payment_card.dart | 8 +- .../widget/recurring_payments_list.dart | 10 +- lib/pages/search/search_page.dart | 40 +- lib/pages/settings/backup/backup_page.dart | 87 +- .../general/general_settings_page.dart | 13 +- .../widgets/currency_selector_dialog.dart | 8 +- .../settings/infos/collaborators_page.dart | 14 +- lib/pages/settings/infos/more_info_page.dart | 10 +- .../settings/infos/privacy_policy_page.dart | 25 +- .../notifications/notifications_settings.dart | 10 +- lib/pages/settings/settings_page.dart | 89 +- lib/pages/structure.dart | 30 +- .../create_transaction_page.dart | 17 +- .../widgets/account_selector.dart | 13 +- .../widgets/amount_section.dart | 13 +- .../widgets/category_selector.dart | 13 +- .../widgets/duplicate_transaction_dialog.dart | 11 +- .../widgets/label_list_tile.dart | 7 +- .../widgets/recurrence_list_tile.dart | 27 +- .../widgets/recurrence_list_tile_edit.dart | 13 +- lib/pages/transactions/transactions_page.dart | 29 +- .../widgets/accounts_pie_chart.dart | 3 +- .../transactions/widgets/accounts_tab.dart | 10 +- .../widgets/add_transaction_card.dart | 8 +- .../widgets/categories_pie_chart.dart | 4 +- .../transactions/widgets/categories_tab.dart | 4 +- .../transactions/widgets/panel_list_tile.dart | 12 +- lib/services/csv/csv_file_picker.dart | 15 +- lib/services/database/sossoldi_database.dart | 38 +- lib/ui/snack_bars/snack_bar.dart | 3 +- .../snack_bars/transactions_snack_bars.dart | 13 +- lib/ui/widgets/alert_dialog.dart | 9 +- lib/ui/widgets/budget_circular_indicator.dart | 3 +- lib/ui/widgets/category_type_button.dart | 8 +- lib/ui/widgets/transaction_type_button.dart | 5 +- lib/ui/widgets/transactions_list.dart | 6 +- pubspec.yaml | 5 +- 77 files changed, 5291 insertions(+), 399 deletions(-) create mode 100644 l10n.yaml create mode 100644 lib/constants/exceptions.dart create mode 100644 lib/l10n/app_en.arb create mode 100644 lib/l10n/app_it.arb create mode 100644 lib/l10n/app_localizations.dart create mode 100644 lib/l10n/app_localizations_en.dart create mode 100644 lib/l10n/app_localizations_it.dart create mode 100644 lib/l10n/app_localizations_pt.dart create mode 100644 lib/l10n/app_pt.arb diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 00000000..aba5d99c --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_it.arb +output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/lib/constants/constants.dart b/lib/constants/constants.dart index 28e9b477..d854712f 100644 --- a/lib/constants/constants.dart +++ b/lib/constants/constants.dart @@ -302,6 +302,8 @@ const String githubUrl = 'https://github.com/RIP-Comm/sossoldi'; const String linkedinUrl = 'https://www.linkedin.com/company/sossoldi'; const String youtubeUrl = 'https://www.youtube.com/@Sossoldi-app'; const String discordUrl = 'https://discord.sossoldi.com'; +// Yeah yap about privacy but still google email you're using +const String sossoldiEmail = 'help.sossoldi@gmail.com'; void updateColorsBasedOnTheme(bool isDarkModeEnabled) { if (isDarkModeEnabled) { diff --git a/lib/constants/exceptions.dart b/lib/constants/exceptions.dart new file mode 100644 index 00000000..f68dd6a3 --- /dev/null +++ b/lib/constants/exceptions.dart @@ -0,0 +1,64 @@ +class CsvExportingErrorException implements Exception { + final String tableName; + CsvExportingErrorException({required this.tableName}); + + @override + String toString() => 'ExportingErrorException: Failed to export table: $tableName'; +} + +class CsvNotFoundException implements Exception { + @override + String toString() => 'CsvNotFoundException: The specified CSV file was not found.'; +} + +class CsvEmptyException implements Exception { + @override + String toString() => 'CsvEmptyException: The CSV file is empty.'; +} + +class CsvExpectedColumnException implements Exception { + final String column; + CsvExpectedColumnException({required this.column}); + + @override + String toString() => 'CsvExpectedColumnException: Missing expected column: $column'; +} + +class CsvUnexpectedValueException implements Exception { + final String value; + CsvUnexpectedValueException({required this.value}); + + @override + String toString() => 'CsvUnexpectedValueException: Found an unexpected value: $value'; +} + +class CsvImportGeneralErrorException implements Exception { + final String text; + CsvImportGeneralErrorException({required this.text}); + @override + String toString() => 'CsvImportGeneralErrorException: A general error occurred during CSV import. Reason: $text'; +} + +class CsvTransactionImportErrorException implements Exception { + final String date; + CsvTransactionImportErrorException({required this.date}); + + @override + String toString() => 'CsvTransactionImportErrorException: Failed to import transaction on date: $date'; +} + +class CleanDatabaseException implements Exception { + final String text; + CleanDatabaseException({required this.text}); + + @override + String toString() => 'CleanDatabaseException: Failed to clean the database. Reason: $text'; +} + +class ResetDatabaseException implements Exception { + final String text; + ResetDatabaseException({required this.text}); + + @override + String toString() => 'ResetDatabaseException: Failed to reset the database. Reason: $text'; +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 00000000..f8a479c1 --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,254 @@ +{ + "@@locale": "en", + "appName": "Sossoldi", + "@appName": { + "description": "The name of the application" + }, + "dashboard": "Dashboard", + "transactions": "Transactions", + "planning": "Planning", + "graphs": "Graphs", + "list": "List", + "categories": "Categories", + "expenses": "Expenses", + "incomes": "Incomes", + "expense": "Expense", + "income": "Income", + "transfer": "Transfer", + "accounts": "Accounts", + "details": "Details", + "account": "Account", + "category": "Category", + "date": "Date", + "investments": "Investments", + "settings": "Settings", + "notifications": "Notifications", + "settingsDisclaimer": "Open source, built by the community", + "addTransaction": "Add transaction", + "totalBalance": "Total balance", + "netWorth": "Net worth", + "save": "Save", + "cancel": "Cancel", + "success": "Success", + "ok" : "Ok", + "editingTransaction": "Editing transaction", + "newTransaction": "New transaction", + "updateTransaction": "Update transaction", + "recurringPayments": "Recurring payments", + "interval": "Interval", + "endRepetition": "End repetition", + "never": "Never", + "onADate": "On a date", + "switchDisabled": "Switch is disabled", + "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", + "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", + "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", + "storagePermissionRequired": "Storage permission is required to access your files.", + "importingData": "Importing data...", + "exportingData": "Exporting data...", + "fileSavedTo": "File saved to: {path}", + "dataImportedSuccessfully": "Data imported successfully", + "description": "Description", + "addDescription": "Add description", + "duplicateTransactionTitle": "Duplicate transaction", + "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", + "duplicate": "Duplicate", + "moreFrequent": "More frequent", + "allCategories": "All categories", + "allAccounts": "All accounts", + "errorOccurred": "Error: {err}", + "selectAccount": "Select Account", + "to": "To:", + "from": "From:", + "recurringTransactionAdded": "Recurring transaction added", + "recurringTransactions": "Recurring transactions", + "addTransactionReminder": "Add transaction reminder", + "privacyPolicyTitle": "Privacy Policy", + "privacyCollectTitle": "What Information Do We Collect?", + "privacyChangesTitle": "Changes to This Privacy Policy", + "contactUsTitle": "Contact us", + "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", + "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", + "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", + "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", + "collaboratorsTitle": "Collaborators", + "meetTheTeam": "Meet the team", + "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", + "wantToContribute": "Want to contribute?", + "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", + "appInfo": "App Info", + "appVersion": "App Version:", + "collaborators": "Collaborators", + "collaboratorsDescription": "See the team behind this app", + "privacyPolicy": "Privacy Policy", + "privacyPolicyDescription": "Read more", + "generalSettings": "General Settings", + "appearance": "Appearance", + "currency": "Currency", + "requireAuthentication": "Require authentication", + "searchForATransaction": "Search for a transaction", + "selectACurrency": "Select a currency", + "search": "Search", + "searchIn": "Search in", + "lastTransactions": "Your last transactions", + "startReconciliation": "Start reconciliation", + "newBalance": "New balance", + "balanceDiscrepancy": "Balance Discrepancy?", + "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", + "newAccount": "New account", + "editAccount": "Edit account", + "createAccount": "Create account", + "accountName": "Account name", + "name": "Name", + "iconAndColor": "Icon and color", + "chooseColor": "Choose color", + "chooseIcon": "Choose icon", + "done": "Fatto", + "add": "Add", + "setAsMainAccount": "Set as main account", + "countsForNetWorth": "Counts for the net worth", + "deleteAccount": "Delete account", + "initialBalance": "Initial balance", + "currentBalance": "Current balance", + "showLess": "Show less", + "showMore": "Show more", + "addSubcategory": "Add subcategory", + "newCategory": "New category", + "editCategory": "Edit category", + "createCategory": "Create category", + "updateCategory": "Update category", + "categoryName": "Category name", + "type": "Type", + "deleteCategory": "Delete category", + "newSubcategory": "New subcategory", + "editSubcategory": "Edit subcategory", + "createSubcategory": "Create subcategory", + "updateSubcategory": "Update subcategory", + "subcategoryName": "Subcategory name", + "deleteSubcategory": "Delete subcategory", + "subcategory": "Subcategory", + "categoryFirstThenBudget": "Add a category first to set a budget", + "inTheNextDays": "In {next} days", + + "monthlyBudget": "Monthly budget", + "manage": "Manage", + "swipeLeftToDelete": "Swipe left to delete", + "yourMonthlyBudgetWillBe": "Your monthly budget will be:", + "saveBudget": "Save budget", + "selectCategoriesToCreateBudget": "Select the categories to create your budget", + "amount": "Amount", + "addCategoryBudget": "Add category budget", + "allCategoriesAdded": "You have already added all available categories.", + "delete": "Delete", + "allRecurringPaymentsHere": "All recurring payments will be displayed here", + "addRecurringPayment": "Add recurring payment", + "seeOlderPayments": "See older payments", + "untilDate": "Until {date}", + "olderPayments": "Older payments", + "categoryNotFound": "Category not found", + "back": "Back", + "onTheDay": "- On the {day} day", + "noMonthlyPaymentHistory": "No monthly payment history", + "noRecurrentPaymentHistory": "No recurrent payment history", + "errorLoadingPayments": "Error loading payments: {error}", + "editRecurringTransaction": "Edit recurring transaction", + "detailsExplanation": "Details (any change will affect only future transactions)", + "dateStart": "Date start", + "planned": "Planned", + "composition": "Composition", + "progress": "Progress", + "noBudgetSet": "There are no budgets set", + "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", + "createBudget": "Create budget", + "setUpTheApp": "Set up the app", + "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", + "startTheSetup": "Start the setup", + "budgetAmount": "Budget {amount}€", + "addBudget": "Add budget", + "addBudgetForCategory": "Add budget for category {cat}", + "addCategory": "Add category", + "confirm": "Confirm", + + "step1Of2": "Step 1 of 2", + "setupMonthlyBudgets": "Set up your monthly\nbudgets", + "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", + "monthlyBudgetTotal": "Monthly budget total:", + "nextStep": "Next step", + "continueWithoutBudget": "Continue without budget", + + "step2Of2": "Step 2 OF 2", + "setLiquidityInMainAccount": "Set the liquidity in your main account", + "addMoreAccounts": "You'll be able to add more accounts within the app.", + "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", + "mainAccount": "Main account", + "setAmount": "Set amount", + "editIconAndColor": "Edit icon and color", + "skipStepOrStartFromZero": "Or you can skip this step and start from 0", + "startTrackingExpenses": "Start tracking your expenses", + "startFromZero": "Start from 0", + + + "importExport": "Import/Export", + "importData": "Import data", + "importDataDescription": "Import a CSV file to update your database", + "importMoneyManager": "Import from Money Manager", + "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", + "exportData": "Export data", + "exportDataDescription": "Save your data as a CSV file", + "warningOverwrite": "Warning: Data Overwrite", + "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", + + "proceedImport": "Proceed with Import", + "importSuccess": "Data imported successfully", + "exportFailed": "Export failed: {err}", + + + "errorExporting": "Failed to export table: {tableName}", + "errorCsvNotFound": "CSV file not found.", + "errorCsvEmpty": "The CSV file is empty.", + "errorCsvExpectedColumn": "Missing expected column: {column}", + "errorCsvUnexpectedValue": "Found an unexpected value: {value}", + "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", + "errorCsvTransactionImport": "Failed to import transaction on date: {date}", + "errorCleanDatabase": "Failed to clean the database. Reason: {error}", + "errorResetDatabase": "Failed to reset the database. Reason: {error}", + + + + "transactionCount": "{count} transactions", + "uncategorized": "Uncategorized", + "noIncomesForSelectedMonth": "No incomes for the selected month", + "noExpensesForSelectedMonth": "No expenses for the selected month", + "total": "Total", + "noTransactionsAdded": "There are no transactions added yet", + "addTransactionCallToAction": "Add a transaction to make this section more appealing", + + "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", + "availableLiquidity": "Available liquidity", + "vsLastMonth": "VS last month", + + "monthlyBalance": "Monthly balance", + "currentMonth": "Current month", + "lastMonth": "Last month", + "yourAccounts": "Your accounts", + "yourBudgets": "Your budgets", + "createBudgetToTrack": "Create a budget to track your spending", + + "close": "Close", + "edit": "Edit", + "errorDuplicatingTransaction": "Error duplicating transaction", + "transactionCreated": "\"{transaction}\" has been created", + "left": "Left", + "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", + "generalSettingsDesc": "Edit general settings", + "accountsDesc": "Add or edit your accounts", + "categoriesDesc": "Add/edit categories and subcategories", + "budget": "Budget", + "budgetDesc": "Add or edit your budgets", + "importExportDesc": "Import or export data", + "notificationsDesc": "Manage your notifications settings", + "leaveFeedback": "Leave a feedback", + "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", + "appInfoDesc": "Learn more about us and the app" + +} \ No newline at end of file diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb new file mode 100644 index 00000000..b03ead95 --- /dev/null +++ b/lib/l10n/app_it.arb @@ -0,0 +1,253 @@ +{ + "@@locale": "it", + "appName": "Sossoldi", + "@appName": { + "description": "Il nome dell'applicazione" + }, + "dashboard": "Dashboard", + "transactions": "Transazioni", + "planning": "Pianificazione", + "graphs": "Grafici", + "list": "Lista", + "categories": "Categorie", + "expenses": "Spese", + "incomes": "Entrate", + "expense": "Spesa", + "income": "Entrata", + "transfer": "Spostamento", + "accounts": "Conti", + "details": "Dettagli", + "account": "Conto", + "category": "Categoria", + "date": "Data", + "investments": "Investimenti", + "settings": "Impostazioni", + "notifications": "Notifiche", + "settingsDisclaimer": "Open source, sviluppata dalla community", + "addTransaction": "Aggiungi transazione", + "totalBalance": "Saldo Totale", + "netWorth": "Patrimonio Netto", + "save": "Salva", + "cancel": "Annulla", + "success": "Successo", + "ok" : "Ok", + "editingTransaction": "Modifica transazione", + "newTransaction": "Nuova transazione", + "updateTransaction": "Modifica transazione", + "recurringPayments": "Pagamenti ricorrenti", + "interval": "Intervallo", + "endRepetition": "Fine ripetizione", + "never": "Mai", + "onADate": "In data", + "switchDisabled": "Gesto disabilitato", + "saveCsvFileFailed": "Non puoi salvare i file qui, crea o seleziona una cartella in Downloads o Documenti. Errore: ${e}", + "errorPickingFile": "Errore durante la selezione del file. Assicurati di avere i permessi necessari. Errore: {error}", + "storagePermissionRequired": "È richiesto il permesso di archiviazione per accedere ai file.", + "importingData": "Importazione dati in corso...", + "exportingData": "Esportazione dati in corso...", + "fileSavedTo": "File salvato in: {path}", + "dataImportedSuccessfully": "Dati importati con successo", + "description": "Descrizione", + "addDescription": "Aggiungi descrizione", + "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", + "duplicateTransactionTitle": "Transazione duplicata", + "duplicateTransactionContent": "Questa transazione è già presente nella lista. Vuoi duplicarla? Potrai poi modificare il nuovo inserimento.", + "duplicate": "Duplica", + "moreFrequent": "Più frequente", + "allCategories": "Tutte le categorie", + "allAccounts": "Tutte i conti", + "errorOccurred": "Errore: {err}", + "selectAccount": "Seleziona conto", + "to": "A:", + "from": "Da:", + "recurringTransactionAdded": "Transazione ricorrente aggiunta", + "recurringTransactions": "Transazioni ricorrenti", + "addTransactionReminder": "Aggiungi promemoria transazione", + "privacyPolicyTitle": "Privacy Policy", + "privacyCollectTitle": "Quali informazioni raccogliamo?", + "privacyChangesTitle": "Modifiche alla Privacy Policy", + "contactUsTitle": "Contattaci", + "privacyIntro": "Sossoldi è sviluppata come un'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n", + "privacyCollectBody": "Sossoldi non raccoglie alcuna informazione personale e non si connette a Internet. Qualsiasi informazione aggiunta nell'app esiste esclusivamente sul tuo dispositivo e da nessun'altra parte.\n", + "privacyChangesBody": "Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n", + "contactUsBody": "Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all'indirizzo\n", + "collaboratorsTitle": "Collaboratori", + "meetTheTeam": "Incontra il team", + "teamDescription": "Sossoldi è sviluppata e mantenuta da una appassionata community open source. Ogni funzione, correzione e idea arriva da persone come te.", + "wantToContribute": "Vuoi contribuire?", + "contributeDescription": "Apri una issue, invia una PR o semplicemente saluta su GitHub", + "appInfo": "Informazioni app", + "appVersion": "Versione app:", + "collaborators": "Collaboratori", + "collaboratorsDescription": "Scopri il team dietro questa app", + "privacyPolicy": "Privacy Policy", + "privacyPolicyDescription": "Leggi di più", + "generalSettings": "Impostazioni generali", + "appearance": "Aspetto", + "currency": "Valuta", + "requireAuthentication": "Richiedi autenticazione", + "searchForATransaction": "Cerca una transazione", + "selectACurrency": "Seleziona una valuta", + "search": "Cerca", + "searchIn": "Cerca in", + "lastTransactions": "Le tue ultime transazioni", + "startReconciliation": "Avvia riconciliazione", + "newBalance": "Nuovo saldo", + "balanceDiscrepancy": "Differenza di saldo?", + "balanceAdjustmentHint": "Il saldo registrato potrebbe differire dall'estratto conto della tua banca. Tocca qui sotto per regolare manualmente il saldo e mantenere i tuoi registri aggiornati.", + "newAccount": "Nuovo conto", + "editAccount": "Modifica conto", + "createAccount": "Crea conto", + "accountName": "Nome del conto", + "name": "Nome", + "iconAndColor": "Icona e colore", + "chooseColor": "Scegli colore", + "chooseIcon": "Scegli icona", + "done": "Fatto", + "add": "Aggiungi", + "setAsMainAccount": "Imposta come conto principale", + "countsForNetWorth": "Includi nel patrimonio netto", + "deleteAccount": "Elimina conto", + "initialBalance": "Saldo iniziale", + "currentBalance": "Saldo attuale", + "showLess": "Mostra meno", + "showMore": "Mostra altro", + "addSubcategory": "Aggiungi sottocategoria", + "newCategory": "Nuova categoria", + "editCategory": "Modifica categoria", + "createCategory": "Crea categoria", + "updateCategory": "Aggiorna categoria", + "categoryName": "Nome della categoria", + "type": "Tipo", + "deleteCategory": "Elimina categoria", + "newSubcategory": "Nuova sottocategoria", + "editSubcategory": "Modifica sottocategoria", + "createSubcategory": "Crea sottocategoria", + "updateSubcategory": "Aggiorna sottocategoria", + "subcategoryName": "Nome della sottocategoria", + "deleteSubcategory": "Elimina sottocategoria", + "subcategory": "Sottocagegoria", + "categoryFirstThenBudget": "Aggiungi una categoria prima di creare un budget", + "inTheNextDays": "In {next} giorni", + + + "monthlyBudget": "Budget mensile", + "manage": "Gestisci", + "swipeLeftToDelete": "Scorri a sinistra per eliminare", + "yourMonthlyBudgetWillBe": "Il tuo budget mensile sarà:", + "saveBudget": "Salva budget", + "selectCategoriesToCreateBudget": "Seleziona le categorie per creare il tuo budget", + "amount": "Importo", + "addCategoryBudget": "Aggiungi budget categoria", + "allCategoriesAdded": "Hai già aggiunto tutte le categorie disponibili.", + "delete": "Elimina", + "allRecurringPaymentsHere": "Tutti i pagamenti ricorrenti verranno visualizzati qui", + "addRecurringPayment": "Aggiungi pagamento ricorrente", + "seeOlderPayments": "Vedi pagamenti passati", + "untilDate": "Fino al {date}", + "olderPayments": "Pagamenti passati", + "categoryNotFound": "Categoria non trovata", + "back": "Indietro", + "onTheDay": "- Il giorno {day}", + "noMonthlyPaymentHistory": "Nessuna cronologia pagamenti mensili", + "noRecurrentPaymentHistory": "Nessuna cronologia pagamenti ricorrenti", + "errorLoadingPayments": "Errore nel caricamento pagamenti: {error}", + "editRecurringTransaction": "Modifica transazione ricorrente", + "detailsExplanation": "Dettagli (ogni modifica influenzerà solo le transazioni future)", + "dateStart": "Data inizio", + "planned": "Pianificato", + "composition": "Composizione", + "progress": "Avanzamento", + "noBudgetSet": "Non ci sono budget impostati", + "budgetHelpText": "Un budget mensile può aiutarti a tenere traccia delle tue spese e a rimanere entro i limiti", + "createBudget": "Crea budget", + "setUpTheApp": "Configura l'app", + "setupDescription": "In pochi passaggi sarai pronto a iniziare a tenere\ntraccia delle tue finanze personali (quasi) come\nMr. Rip.", + "startTheSetup": "Inizia la configurazione", + "budgetAmount": "Budget {amount}€", + "addBudget": "Aggiungi budget", + "addBudgetForCategory": "Aggiungi un budget per la categoria {cat}", + "addCategory": "Aggiungi categoria", + "confirm": "Conferma", + + "step1Of2": "Passaggio 1 di 2", + "setupMonthlyBudgets": "Imposta i tuoi budget\nmensili", + "chooseCategoriesForBudget": "Scegli le categorie per le quali vuoi impostare un budget", + "monthlyBudgetTotal": "Totale budget mensile:", + "nextStep": "Passaggio successivo", + "continueWithoutBudget": "Continua senza budget", + + "step2Of2": "Passaggio 2 DI 2", + "setLiquidityInMainAccount": "Imposta la liquidità nel tuo conto principale", + "addMoreAccounts": "Sarai in grado di aggiungere altri account dall'app", + "liquidityDescription": "Verrà utilizzata come base a cui aggiungere entrate, spese e calcolare il tuo patrimonio.\nPotrai aggiungere altri conti all'interno dell'app.", + "mainAccount": "Conto principale", + "setAmount": "Imposta importo", + "editIconAndColor": "Modifica icona e colore", + "skipStepOrStartFromZero": "Oppure puoi saltare questo passaggio e iniziare da 0", + "startTrackingExpenses": "Inizia a tracciare le tue spese", + "startFromZero": "Inizia da 0", + + "importExport": "Importa/Esporta", + "importData": "Importa dati", + "importDataDescription": "Importa un file CSV per aggiornare il database", + "importMoneyManager": "Importa da Money Manager", + "importMoneyManagerDescription": "Importa CSV da Money Manager per aggiornare il database. Il file deve essere salvato in formato CSV da XLS.", + "exportData": "Esporta dati", + "exportDataDescription": "Salva i tuoi dati come file CSV", + "warningOverwrite": "Attenzione: Sovrascrittura dati", + "warningOverwriteContent": "L'importazione di questo file sostituirà definitivamente i tuoi dati esistenti. Questa azione non può essere annullata. Assicurati di avere un backup prima di procedere.", + + "proceedImport": "Procedi con l'importazione", + "importSuccess": "Dati importati con successo", + "exportFailed": "Esportazione fallita: {err}", + + + "errorExporting": "Impossibile esportare la tabella: {tableName}", + "errorCsvNotFound": "File CSV non trovato.", + "errorCsvEmpty": "Il file CSV è vuoto.", + "errorCsvExpectedColumn": "Colonna mancante nel CSV: {column}", + "errorCsvUnexpectedValue": "Valore non previsto trovato: {value}", + "errorCsvImportGeneral": "Si è verificato un errore generale durante l'importazione del CSV. Errore: {error}", + "errorCsvTransactionImport": "Errore durante l'importazione della transazione in data: {date}", + "errorCleanDatabase": "Impossibile pulire il database. Motivo: {error}", + "errorResetDatabase": "Impossibile ripristinare il database. Motivo: {error}", + + "transactionCount": "{count} transazioni", + "uncategorized": "Senza categoria", + "noIncomesForSelectedMonth": "Nessuna entrata per il mese selezionato", + "noExpensesForSelectedMonth": "Nessuna spesa per il mese selezionato", + "total": "Totale", + "noTransactionsAdded": "Non ci sono ancora transazioni aggiunte", + "addTransactionCallToAction": "Aggiungi una transazione per rendere questa sezione più interessante", + + "graphsEmptyState": "Dopo aver aggiunto alcune transazioni, dei grafici eccezionali appariranno qui... quasi per magia!", + "availableLiquidity": "Liquidità disponibile", + "vsLastMonth": "VS mese scorso", + + "monthlyBalance": "Bilancio mensile", + "currentMonth": "Mese corrente", + "lastMonth": "Mese scorso", + "yourAccounts": "I tuoi conti", + "yourBudgets": "I tuoi budget", + "createBudgetToTrack": "Crea un budget per monitorare le tue spese", + + "close": "Chiudi", + "edit": "Modifica", + "errorDuplicatingTransaction": "Errore durante la duplicazione della transazione", + "transactionCreated": "\"{transaction}\" è stata creata", + "left": "Rimanenti", + "notEnoughDataForGraph": "Siamo spiacenti, ma non ci sono\nabbastanza dati per creare il grafico...", + + "generalSettingsDesc": "Modifica impostazioni generali", + "accountsDesc": "Aggiungi o modifica i tuoi conti", + "categoriesDesc": "Aggiungi/modifica categorie e sottocategorie", + "budget": "Budget", + "budgetDesc": "Aggiungi o modifica i tuoi budget", + "importExportDesc": "Importa o esporta dati", + "notificationsDesc": "Gestisci le impostazioni delle notifiche", + "leaveFeedback": "Lascia un feedback", + "leaveFeedbackDesc": "Compila un modulo per segnalare un bug o lasciare un feedback", + "appInfoDesc": "Scopri di più su di noi e sull'app" + +} \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart new file mode 100644 index 00000000..69cd8d69 --- /dev/null +++ b/lib/l10n/app_localizations.dart @@ -0,0 +1,1536 @@ +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'; +import 'app_localizations_it.dart'; +import 'app_localizations_pt.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'), + Locale('it'), + Locale('pt'), + ]; + + /// Il nome dell'applicazione + /// + /// In it, this message translates to: + /// **'Sossoldi'** + String get appName; + + /// No description provided for @dashboard. + /// + /// In it, this message translates to: + /// **'Dashboard'** + String get dashboard; + + /// No description provided for @transactions. + /// + /// In it, this message translates to: + /// **'Transazioni'** + String get transactions; + + /// No description provided for @planning. + /// + /// In it, this message translates to: + /// **'Pianificazione'** + String get planning; + + /// No description provided for @graphs. + /// + /// In it, this message translates to: + /// **'Grafici'** + String get graphs; + + /// No description provided for @list. + /// + /// In it, this message translates to: + /// **'Lista'** + String get list; + + /// No description provided for @categories. + /// + /// In it, this message translates to: + /// **'Categorie'** + String get categories; + + /// No description provided for @expenses. + /// + /// In it, this message translates to: + /// **'Spese'** + String get expenses; + + /// No description provided for @incomes. + /// + /// In it, this message translates to: + /// **'Entrate'** + String get incomes; + + /// No description provided for @expense. + /// + /// In it, this message translates to: + /// **'Spesa'** + String get expense; + + /// No description provided for @income. + /// + /// In it, this message translates to: + /// **'Entrata'** + String get income; + + /// No description provided for @transfer. + /// + /// In it, this message translates to: + /// **'Spostamento'** + String get transfer; + + /// No description provided for @accounts. + /// + /// In it, this message translates to: + /// **'Conti'** + String get accounts; + + /// No description provided for @details. + /// + /// In it, this message translates to: + /// **'Dettagli'** + String get details; + + /// No description provided for @account. + /// + /// In it, this message translates to: + /// **'Conto'** + String get account; + + /// No description provided for @category. + /// + /// In it, this message translates to: + /// **'Categoria'** + String get category; + + /// No description provided for @date. + /// + /// In it, this message translates to: + /// **'Data'** + String get date; + + /// No description provided for @investments. + /// + /// In it, this message translates to: + /// **'Investimenti'** + String get investments; + + /// No description provided for @settings. + /// + /// In it, this message translates to: + /// **'Impostazioni'** + String get settings; + + /// No description provided for @notifications. + /// + /// In it, this message translates to: + /// **'Notifiche'** + String get notifications; + + /// No description provided for @settingsDisclaimer. + /// + /// In it, this message translates to: + /// **'Open source, sviluppata dalla community'** + String get settingsDisclaimer; + + /// No description provided for @addTransaction. + /// + /// In it, this message translates to: + /// **'Aggiungi transazione'** + String get addTransaction; + + /// No description provided for @totalBalance. + /// + /// In it, this message translates to: + /// **'Saldo Totale'** + String get totalBalance; + + /// No description provided for @netWorth. + /// + /// In it, this message translates to: + /// **'Patrimonio Netto'** + String get netWorth; + + /// No description provided for @save. + /// + /// In it, this message translates to: + /// **'Salva'** + String get save; + + /// No description provided for @cancel. + /// + /// In it, this message translates to: + /// **'Annulla'** + String get cancel; + + /// No description provided for @success. + /// + /// In it, this message translates to: + /// **'Successo'** + String get success; + + /// No description provided for @ok. + /// + /// In it, this message translates to: + /// **'Ok'** + String get ok; + + /// No description provided for @editingTransaction. + /// + /// In it, this message translates to: + /// **'Modifica transazione'** + String get editingTransaction; + + /// No description provided for @newTransaction. + /// + /// In it, this message translates to: + /// **'Nuova transazione'** + String get newTransaction; + + /// No description provided for @updateTransaction. + /// + /// In it, this message translates to: + /// **'Modifica transazione'** + String get updateTransaction; + + /// No description provided for @recurringPayments. + /// + /// In it, this message translates to: + /// **'Pagamenti ricorrenti'** + String get recurringPayments; + + /// No description provided for @interval. + /// + /// In it, this message translates to: + /// **'Intervallo'** + String get interval; + + /// No description provided for @endRepetition. + /// + /// In it, this message translates to: + /// **'Fine ripetizione'** + String get endRepetition; + + /// No description provided for @never. + /// + /// In it, this message translates to: + /// **'Mai'** + String get never; + + /// No description provided for @onADate. + /// + /// In it, this message translates to: + /// **'In data'** + String get onADate; + + /// No description provided for @switchDisabled. + /// + /// In it, this message translates to: + /// **'Gesto disabilitato'** + String get switchDisabled; + + /// No description provided for @saveCsvFileFailed. + /// + /// In it, this message translates to: + /// **'Non puoi salvare i file qui, crea o seleziona una cartella in Downloads o Documenti. Errore: \${e}'** + String saveCsvFileFailed(Object e); + + /// No description provided for @errorPickingFile. + /// + /// In it, this message translates to: + /// **'Errore durante la selezione del file. Assicurati di avere i permessi necessari. Errore: {error}'** + String errorPickingFile(Object error); + + /// No description provided for @storagePermissionRequired. + /// + /// In it, this message translates to: + /// **'È richiesto il permesso di archiviazione per accedere ai file.'** + String get storagePermissionRequired; + + /// No description provided for @importingData. + /// + /// In it, this message translates to: + /// **'Importazione dati in corso...'** + String get importingData; + + /// No description provided for @exportingData. + /// + /// In it, this message translates to: + /// **'Esportazione dati in corso...'** + String get exportingData; + + /// No description provided for @fileSavedTo. + /// + /// In it, this message translates to: + /// **'File salvato in: {path}'** + String fileSavedTo(Object path); + + /// No description provided for @dataImportedSuccessfully. + /// + /// In it, this message translates to: + /// **'Dati importati con successo'** + String get dataImportedSuccessfully; + + /// No description provided for @description. + /// + /// In it, this message translates to: + /// **'Descrizione'** + String get description; + + /// No description provided for @addDescription. + /// + /// In it, this message translates to: + /// **'Aggiungi descrizione'** + String get addDescription; + + /// No description provided for @recurringTransactionWarning. + /// + /// In it, this message translates to: + /// **'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'** + String get recurringTransactionWarning; + + /// No description provided for @duplicateTransactionTitle. + /// + /// In it, this message translates to: + /// **'Transazione duplicata'** + String get duplicateTransactionTitle; + + /// No description provided for @duplicateTransactionContent. + /// + /// In it, this message translates to: + /// **'Questa transazione è già presente nella lista. Vuoi duplicarla? Potrai poi modificare il nuovo inserimento.'** + String get duplicateTransactionContent; + + /// No description provided for @duplicate. + /// + /// In it, this message translates to: + /// **'Duplica'** + String get duplicate; + + /// No description provided for @moreFrequent. + /// + /// In it, this message translates to: + /// **'Più frequente'** + String get moreFrequent; + + /// No description provided for @allCategories. + /// + /// In it, this message translates to: + /// **'Tutte le categorie'** + String get allCategories; + + /// No description provided for @allAccounts. + /// + /// In it, this message translates to: + /// **'Tutte i conti'** + String get allAccounts; + + /// No description provided for @errorOccurred. + /// + /// In it, this message translates to: + /// **'Errore: {err}'** + String errorOccurred(Object err); + + /// No description provided for @selectAccount. + /// + /// In it, this message translates to: + /// **'Seleziona conto'** + String get selectAccount; + + /// No description provided for @to. + /// + /// In it, this message translates to: + /// **'A:'** + String get to; + + /// No description provided for @from. + /// + /// In it, this message translates to: + /// **'Da:'** + String get from; + + /// No description provided for @recurringTransactionAdded. + /// + /// In it, this message translates to: + /// **'Transazione ricorrente aggiunta'** + String get recurringTransactionAdded; + + /// No description provided for @recurringTransactions. + /// + /// In it, this message translates to: + /// **'Transazioni ricorrenti'** + String get recurringTransactions; + + /// No description provided for @addTransactionReminder. + /// + /// In it, this message translates to: + /// **'Aggiungi promemoria transazione'** + String get addTransactionReminder; + + /// No description provided for @privacyPolicyTitle. + /// + /// In it, this message translates to: + /// **'Privacy Policy'** + String get privacyPolicyTitle; + + /// No description provided for @privacyCollectTitle. + /// + /// In it, this message translates to: + /// **'Quali informazioni raccogliamo?'** + String get privacyCollectTitle; + + /// No description provided for @privacyChangesTitle. + /// + /// In it, this message translates to: + /// **'Modifiche alla Privacy Policy'** + String get privacyChangesTitle; + + /// No description provided for @contactUsTitle. + /// + /// In it, this message translates to: + /// **'Contattaci'** + String get contactUsTitle; + + /// No description provided for @privacyIntro. + /// + /// In it, this message translates to: + /// **'Sossoldi è sviluppata come un\'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'** + String get privacyIntro; + + /// No description provided for @privacyCollectBody. + /// + /// In it, this message translates to: + /// **'Sossoldi non raccoglie alcuna informazione personale e non si connette a Internet. Qualsiasi informazione aggiunta nell\'app esiste esclusivamente sul tuo dispositivo e da nessun\'altra parte.\n'** + String get privacyCollectBody; + + /// No description provided for @privacyChangesBody. + /// + /// In it, this message translates to: + /// **'Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n'** + String get privacyChangesBody; + + /// No description provided for @contactUsBody. + /// + /// In it, this message translates to: + /// **'Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all\'indirizzo\n'** + String get contactUsBody; + + /// No description provided for @collaboratorsTitle. + /// + /// In it, this message translates to: + /// **'Collaboratori'** + String get collaboratorsTitle; + + /// No description provided for @meetTheTeam. + /// + /// In it, this message translates to: + /// **'Incontra il team'** + String get meetTheTeam; + + /// No description provided for @teamDescription. + /// + /// In it, this message translates to: + /// **'Sossoldi è sviluppata e mantenuta da una appassionata community open source. Ogni funzione, correzione e idea arriva da persone come te.'** + String get teamDescription; + + /// No description provided for @wantToContribute. + /// + /// In it, this message translates to: + /// **'Vuoi contribuire?'** + String get wantToContribute; + + /// No description provided for @contributeDescription. + /// + /// In it, this message translates to: + /// **'Apri una issue, invia una PR o semplicemente saluta su GitHub'** + String get contributeDescription; + + /// No description provided for @appInfo. + /// + /// In it, this message translates to: + /// **'Informazioni app'** + String get appInfo; + + /// No description provided for @appVersion. + /// + /// In it, this message translates to: + /// **'Versione app:'** + String get appVersion; + + /// No description provided for @collaborators. + /// + /// In it, this message translates to: + /// **'Collaboratori'** + String get collaborators; + + /// No description provided for @collaboratorsDescription. + /// + /// In it, this message translates to: + /// **'Scopri il team dietro questa app'** + String get collaboratorsDescription; + + /// No description provided for @privacyPolicy. + /// + /// In it, this message translates to: + /// **'Privacy Policy'** + String get privacyPolicy; + + /// No description provided for @privacyPolicyDescription. + /// + /// In it, this message translates to: + /// **'Leggi di più'** + String get privacyPolicyDescription; + + /// No description provided for @generalSettings. + /// + /// In it, this message translates to: + /// **'Impostazioni generali'** + String get generalSettings; + + /// No description provided for @appearance. + /// + /// In it, this message translates to: + /// **'Aspetto'** + String get appearance; + + /// No description provided for @currency. + /// + /// In it, this message translates to: + /// **'Valuta'** + String get currency; + + /// No description provided for @requireAuthentication. + /// + /// In it, this message translates to: + /// **'Richiedi autenticazione'** + String get requireAuthentication; + + /// No description provided for @searchForATransaction. + /// + /// In it, this message translates to: + /// **'Cerca una transazione'** + String get searchForATransaction; + + /// No description provided for @selectACurrency. + /// + /// In it, this message translates to: + /// **'Seleziona una valuta'** + String get selectACurrency; + + /// No description provided for @search. + /// + /// In it, this message translates to: + /// **'Cerca'** + String get search; + + /// No description provided for @searchIn. + /// + /// In it, this message translates to: + /// **'Cerca in'** + String get searchIn; + + /// No description provided for @lastTransactions. + /// + /// In it, this message translates to: + /// **'Le tue ultime transazioni'** + String get lastTransactions; + + /// No description provided for @startReconciliation. + /// + /// In it, this message translates to: + /// **'Avvia riconciliazione'** + String get startReconciliation; + + /// No description provided for @newBalance. + /// + /// In it, this message translates to: + /// **'Nuovo saldo'** + String get newBalance; + + /// No description provided for @balanceDiscrepancy. + /// + /// In it, this message translates to: + /// **'Differenza di saldo?'** + String get balanceDiscrepancy; + + /// No description provided for @balanceAdjustmentHint. + /// + /// In it, this message translates to: + /// **'Il saldo registrato potrebbe differire dall\'estratto conto della tua banca. Tocca qui sotto per regolare manualmente il saldo e mantenere i tuoi registri aggiornati.'** + String get balanceAdjustmentHint; + + /// No description provided for @newAccount. + /// + /// In it, this message translates to: + /// **'Nuovo conto'** + String get newAccount; + + /// No description provided for @editAccount. + /// + /// In it, this message translates to: + /// **'Modifica conto'** + String get editAccount; + + /// No description provided for @createAccount. + /// + /// In it, this message translates to: + /// **'Crea conto'** + String get createAccount; + + /// No description provided for @accountName. + /// + /// In it, this message translates to: + /// **'Nome del conto'** + String get accountName; + + /// No description provided for @name. + /// + /// In it, this message translates to: + /// **'Nome'** + String get name; + + /// No description provided for @iconAndColor. + /// + /// In it, this message translates to: + /// **'Icona e colore'** + String get iconAndColor; + + /// No description provided for @chooseColor. + /// + /// In it, this message translates to: + /// **'Scegli colore'** + String get chooseColor; + + /// No description provided for @chooseIcon. + /// + /// In it, this message translates to: + /// **'Scegli icona'** + String get chooseIcon; + + /// No description provided for @done. + /// + /// In it, this message translates to: + /// **'Fatto'** + String get done; + + /// No description provided for @add. + /// + /// In it, this message translates to: + /// **'Aggiungi'** + String get add; + + /// No description provided for @setAsMainAccount. + /// + /// In it, this message translates to: + /// **'Imposta come conto principale'** + String get setAsMainAccount; + + /// No description provided for @countsForNetWorth. + /// + /// In it, this message translates to: + /// **'Includi nel patrimonio netto'** + String get countsForNetWorth; + + /// No description provided for @deleteAccount. + /// + /// In it, this message translates to: + /// **'Elimina conto'** + String get deleteAccount; + + /// No description provided for @initialBalance. + /// + /// In it, this message translates to: + /// **'Saldo iniziale'** + String get initialBalance; + + /// No description provided for @currentBalance. + /// + /// In it, this message translates to: + /// **'Saldo attuale'** + String get currentBalance; + + /// No description provided for @showLess. + /// + /// In it, this message translates to: + /// **'Mostra meno'** + String get showLess; + + /// No description provided for @showMore. + /// + /// In it, this message translates to: + /// **'Mostra altro'** + String get showMore; + + /// No description provided for @addSubcategory. + /// + /// In it, this message translates to: + /// **'Aggiungi sottocategoria'** + String get addSubcategory; + + /// No description provided for @newCategory. + /// + /// In it, this message translates to: + /// **'Nuova categoria'** + String get newCategory; + + /// No description provided for @editCategory. + /// + /// In it, this message translates to: + /// **'Modifica categoria'** + String get editCategory; + + /// No description provided for @createCategory. + /// + /// In it, this message translates to: + /// **'Crea categoria'** + String get createCategory; + + /// No description provided for @updateCategory. + /// + /// In it, this message translates to: + /// **'Aggiorna categoria'** + String get updateCategory; + + /// No description provided for @categoryName. + /// + /// In it, this message translates to: + /// **'Nome della categoria'** + String get categoryName; + + /// No description provided for @type. + /// + /// In it, this message translates to: + /// **'Tipo'** + String get type; + + /// No description provided for @deleteCategory. + /// + /// In it, this message translates to: + /// **'Elimina categoria'** + String get deleteCategory; + + /// No description provided for @newSubcategory. + /// + /// In it, this message translates to: + /// **'Nuova sottocategoria'** + String get newSubcategory; + + /// No description provided for @editSubcategory. + /// + /// In it, this message translates to: + /// **'Modifica sottocategoria'** + String get editSubcategory; + + /// No description provided for @createSubcategory. + /// + /// In it, this message translates to: + /// **'Crea sottocategoria'** + String get createSubcategory; + + /// No description provided for @updateSubcategory. + /// + /// In it, this message translates to: + /// **'Aggiorna sottocategoria'** + String get updateSubcategory; + + /// No description provided for @subcategoryName. + /// + /// In it, this message translates to: + /// **'Nome della sottocategoria'** + String get subcategoryName; + + /// No description provided for @deleteSubcategory. + /// + /// In it, this message translates to: + /// **'Elimina sottocategoria'** + String get deleteSubcategory; + + /// No description provided for @subcategory. + /// + /// In it, this message translates to: + /// **'Sottocagegoria'** + String get subcategory; + + /// No description provided for @categoryFirstThenBudget. + /// + /// In it, this message translates to: + /// **'Aggiungi una categoria prima di creare un budget'** + String get categoryFirstThenBudget; + + /// No description provided for @inTheNextDays. + /// + /// In it, this message translates to: + /// **'In {next} giorni'** + String inTheNextDays(Object next); + + /// No description provided for @monthlyBudget. + /// + /// In it, this message translates to: + /// **'Budget mensile'** + String get monthlyBudget; + + /// No description provided for @manage. + /// + /// In it, this message translates to: + /// **'Gestisci'** + String get manage; + + /// No description provided for @swipeLeftToDelete. + /// + /// In it, this message translates to: + /// **'Scorri a sinistra per eliminare'** + String get swipeLeftToDelete; + + /// No description provided for @yourMonthlyBudgetWillBe. + /// + /// In it, this message translates to: + /// **'Il tuo budget mensile sarà:'** + String get yourMonthlyBudgetWillBe; + + /// No description provided for @saveBudget. + /// + /// In it, this message translates to: + /// **'Salva budget'** + String get saveBudget; + + /// No description provided for @selectCategoriesToCreateBudget. + /// + /// In it, this message translates to: + /// **'Seleziona le categorie per creare il tuo budget'** + String get selectCategoriesToCreateBudget; + + /// No description provided for @amount. + /// + /// In it, this message translates to: + /// **'Importo'** + String get amount; + + /// No description provided for @addCategoryBudget. + /// + /// In it, this message translates to: + /// **'Aggiungi budget categoria'** + String get addCategoryBudget; + + /// No description provided for @allCategoriesAdded. + /// + /// In it, this message translates to: + /// **'Hai già aggiunto tutte le categorie disponibili.'** + String get allCategoriesAdded; + + /// No description provided for @delete. + /// + /// In it, this message translates to: + /// **'Elimina'** + String get delete; + + /// No description provided for @allRecurringPaymentsHere. + /// + /// In it, this message translates to: + /// **'Tutti i pagamenti ricorrenti verranno visualizzati qui'** + String get allRecurringPaymentsHere; + + /// No description provided for @addRecurringPayment. + /// + /// In it, this message translates to: + /// **'Aggiungi pagamento ricorrente'** + String get addRecurringPayment; + + /// No description provided for @seeOlderPayments. + /// + /// In it, this message translates to: + /// **'Vedi pagamenti passati'** + String get seeOlderPayments; + + /// No description provided for @untilDate. + /// + /// In it, this message translates to: + /// **'Fino al {date}'** + String untilDate(Object date); + + /// No description provided for @olderPayments. + /// + /// In it, this message translates to: + /// **'Pagamenti passati'** + String get olderPayments; + + /// No description provided for @categoryNotFound. + /// + /// In it, this message translates to: + /// **'Categoria non trovata'** + String get categoryNotFound; + + /// No description provided for @back. + /// + /// In it, this message translates to: + /// **'Indietro'** + String get back; + + /// No description provided for @onTheDay. + /// + /// In it, this message translates to: + /// **'- Il giorno {day}'** + String onTheDay(Object day); + + /// No description provided for @noMonthlyPaymentHistory. + /// + /// In it, this message translates to: + /// **'Nessuna cronologia pagamenti mensili'** + String get noMonthlyPaymentHistory; + + /// No description provided for @noRecurrentPaymentHistory. + /// + /// In it, this message translates to: + /// **'Nessuna cronologia pagamenti ricorrenti'** + String get noRecurrentPaymentHistory; + + /// No description provided for @errorLoadingPayments. + /// + /// In it, this message translates to: + /// **'Errore nel caricamento pagamenti: {error}'** + String errorLoadingPayments(Object error); + + /// No description provided for @editRecurringTransaction. + /// + /// In it, this message translates to: + /// **'Modifica transazione ricorrente'** + String get editRecurringTransaction; + + /// No description provided for @detailsExplanation. + /// + /// In it, this message translates to: + /// **'Dettagli (ogni modifica influenzerà solo le transazioni future)'** + String get detailsExplanation; + + /// No description provided for @dateStart. + /// + /// In it, this message translates to: + /// **'Data inizio'** + String get dateStart; + + /// No description provided for @planned. + /// + /// In it, this message translates to: + /// **'Pianificato'** + String get planned; + + /// No description provided for @composition. + /// + /// In it, this message translates to: + /// **'Composizione'** + String get composition; + + /// No description provided for @progress. + /// + /// In it, this message translates to: + /// **'Avanzamento'** + String get progress; + + /// No description provided for @noBudgetSet. + /// + /// In it, this message translates to: + /// **'Non ci sono budget impostati'** + String get noBudgetSet; + + /// No description provided for @budgetHelpText. + /// + /// In it, this message translates to: + /// **'Un budget mensile può aiutarti a tenere traccia delle tue spese e a rimanere entro i limiti'** + String get budgetHelpText; + + /// No description provided for @createBudget. + /// + /// In it, this message translates to: + /// **'Crea budget'** + String get createBudget; + + /// No description provided for @setUpTheApp. + /// + /// In it, this message translates to: + /// **'Configura l\'app'** + String get setUpTheApp; + + /// No description provided for @setupDescription. + /// + /// In it, this message translates to: + /// **'In pochi passaggi sarai pronto a iniziare a tenere\ntraccia delle tue finanze personali (quasi) come\nMr. Rip.'** + String get setupDescription; + + /// No description provided for @startTheSetup. + /// + /// In it, this message translates to: + /// **'Inizia la configurazione'** + String get startTheSetup; + + /// No description provided for @budgetAmount. + /// + /// In it, this message translates to: + /// **'Budget {amount}€'** + String budgetAmount(Object amount); + + /// No description provided for @addBudget. + /// + /// In it, this message translates to: + /// **'Aggiungi budget'** + String get addBudget; + + /// No description provided for @addBudgetForCategory. + /// + /// In it, this message translates to: + /// **'Aggiungi un budget per la categoria {cat}'** + String addBudgetForCategory(Object cat); + + /// No description provided for @addCategory. + /// + /// In it, this message translates to: + /// **'Aggiungi categoria'** + String get addCategory; + + /// No description provided for @confirm. + /// + /// In it, this message translates to: + /// **'Conferma'** + String get confirm; + + /// No description provided for @step1Of2. + /// + /// In it, this message translates to: + /// **'Passaggio 1 di 2'** + String get step1Of2; + + /// No description provided for @setupMonthlyBudgets. + /// + /// In it, this message translates to: + /// **'Imposta i tuoi budget\nmensili'** + String get setupMonthlyBudgets; + + /// No description provided for @chooseCategoriesForBudget. + /// + /// In it, this message translates to: + /// **'Scegli le categorie per le quali vuoi impostare un budget'** + String get chooseCategoriesForBudget; + + /// No description provided for @monthlyBudgetTotal. + /// + /// In it, this message translates to: + /// **'Totale budget mensile:'** + String get monthlyBudgetTotal; + + /// No description provided for @nextStep. + /// + /// In it, this message translates to: + /// **'Passaggio successivo'** + String get nextStep; + + /// No description provided for @continueWithoutBudget. + /// + /// In it, this message translates to: + /// **'Continua senza budget'** + String get continueWithoutBudget; + + /// No description provided for @step2Of2. + /// + /// In it, this message translates to: + /// **'Passaggio 2 DI 2'** + String get step2Of2; + + /// No description provided for @setLiquidityInMainAccount. + /// + /// In it, this message translates to: + /// **'Imposta la liquidità nel tuo conto principale'** + String get setLiquidityInMainAccount; + + /// No description provided for @addMoreAccounts. + /// + /// In it, this message translates to: + /// **'Sarai in grado di aggiungere altri account dall\'app'** + String get addMoreAccounts; + + /// No description provided for @liquidityDescription. + /// + /// In it, this message translates to: + /// **'Verrà utilizzata come base a cui aggiungere entrate, spese e calcolare il tuo patrimonio.\nPotrai aggiungere altri conti all\'interno dell\'app.'** + String get liquidityDescription; + + /// No description provided for @mainAccount. + /// + /// In it, this message translates to: + /// **'Conto principale'** + String get mainAccount; + + /// No description provided for @setAmount. + /// + /// In it, this message translates to: + /// **'Imposta importo'** + String get setAmount; + + /// No description provided for @editIconAndColor. + /// + /// In it, this message translates to: + /// **'Modifica icona e colore'** + String get editIconAndColor; + + /// No description provided for @skipStepOrStartFromZero. + /// + /// In it, this message translates to: + /// **'Oppure puoi saltare questo passaggio e iniziare da 0'** + String get skipStepOrStartFromZero; + + /// No description provided for @startTrackingExpenses. + /// + /// In it, this message translates to: + /// **'Inizia a tracciare le tue spese'** + String get startTrackingExpenses; + + /// No description provided for @startFromZero. + /// + /// In it, this message translates to: + /// **'Inizia da 0'** + String get startFromZero; + + /// No description provided for @importExport. + /// + /// In it, this message translates to: + /// **'Importa/Esporta'** + String get importExport; + + /// No description provided for @importData. + /// + /// In it, this message translates to: + /// **'Importa dati'** + String get importData; + + /// No description provided for @importDataDescription. + /// + /// In it, this message translates to: + /// **'Importa un file CSV per aggiornare il database'** + String get importDataDescription; + + /// No description provided for @importMoneyManager. + /// + /// In it, this message translates to: + /// **'Importa da Money Manager'** + String get importMoneyManager; + + /// No description provided for @importMoneyManagerDescription. + /// + /// In it, this message translates to: + /// **'Importa CSV da Money Manager per aggiornare il database. Il file deve essere salvato in formato CSV da XLS.'** + String get importMoneyManagerDescription; + + /// No description provided for @exportData. + /// + /// In it, this message translates to: + /// **'Esporta dati'** + String get exportData; + + /// No description provided for @exportDataDescription. + /// + /// In it, this message translates to: + /// **'Salva i tuoi dati come file CSV'** + String get exportDataDescription; + + /// No description provided for @warningOverwrite. + /// + /// In it, this message translates to: + /// **'Attenzione: Sovrascrittura dati'** + String get warningOverwrite; + + /// No description provided for @warningOverwriteContent. + /// + /// In it, this message translates to: + /// **'L\'importazione di questo file sostituirà definitivamente i tuoi dati esistenti. Questa azione non può essere annullata. Assicurati di avere un backup prima di procedere.'** + String get warningOverwriteContent; + + /// No description provided for @proceedImport. + /// + /// In it, this message translates to: + /// **'Procedi con l\'importazione'** + String get proceedImport; + + /// No description provided for @importSuccess. + /// + /// In it, this message translates to: + /// **'Dati importati con successo'** + String get importSuccess; + + /// No description provided for @exportFailed. + /// + /// In it, this message translates to: + /// **'Esportazione fallita: {err}'** + String exportFailed(Object err); + + /// No description provided for @errorExporting. + /// + /// In it, this message translates to: + /// **'Impossibile esportare la tabella: {tableName}'** + String errorExporting(Object tableName); + + /// No description provided for @errorCsvNotFound. + /// + /// In it, this message translates to: + /// **'File CSV non trovato.'** + String get errorCsvNotFound; + + /// No description provided for @errorCsvEmpty. + /// + /// In it, this message translates to: + /// **'Il file CSV è vuoto.'** + String get errorCsvEmpty; + + /// No description provided for @errorCsvExpectedColumn. + /// + /// In it, this message translates to: + /// **'Colonna mancante nel CSV: {column}'** + String errorCsvExpectedColumn(Object column); + + /// No description provided for @errorCsvUnexpectedValue. + /// + /// In it, this message translates to: + /// **'Valore non previsto trovato: {value}'** + String errorCsvUnexpectedValue(Object value); + + /// No description provided for @errorCsvImportGeneral. + /// + /// In it, this message translates to: + /// **'Si è verificato un errore generale durante l\'importazione del CSV. Errore: {error}'** + String errorCsvImportGeneral(Object error); + + /// No description provided for @errorCsvTransactionImport. + /// + /// In it, this message translates to: + /// **'Errore durante l\'importazione della transazione in data: {date}'** + String errorCsvTransactionImport(Object date); + + /// No description provided for @errorCleanDatabase. + /// + /// In it, this message translates to: + /// **'Impossibile pulire il database. Motivo: {error}'** + String errorCleanDatabase(Object error); + + /// No description provided for @errorResetDatabase. + /// + /// In it, this message translates to: + /// **'Impossibile ripristinare il database. Motivo: {error}'** + String errorResetDatabase(Object error); + + /// No description provided for @transactionCount. + /// + /// In it, this message translates to: + /// **'{count} transazioni'** + String transactionCount(Object count); + + /// No description provided for @uncategorized. + /// + /// In it, this message translates to: + /// **'Senza categoria'** + String get uncategorized; + + /// No description provided for @noIncomesForSelectedMonth. + /// + /// In it, this message translates to: + /// **'Nessuna entrata per il mese selezionato'** + String get noIncomesForSelectedMonth; + + /// No description provided for @noExpensesForSelectedMonth. + /// + /// In it, this message translates to: + /// **'Nessuna spesa per il mese selezionato'** + String get noExpensesForSelectedMonth; + + /// No description provided for @total. + /// + /// In it, this message translates to: + /// **'Totale'** + String get total; + + /// No description provided for @noTransactionsAdded. + /// + /// In it, this message translates to: + /// **'Non ci sono ancora transazioni aggiunte'** + String get noTransactionsAdded; + + /// No description provided for @addTransactionCallToAction. + /// + /// In it, this message translates to: + /// **'Aggiungi una transazione per rendere questa sezione più interessante'** + String get addTransactionCallToAction; + + /// No description provided for @graphsEmptyState. + /// + /// In it, this message translates to: + /// **'Dopo aver aggiunto alcune transazioni, dei grafici eccezionali appariranno qui... quasi per magia!'** + String get graphsEmptyState; + + /// No description provided for @availableLiquidity. + /// + /// In it, this message translates to: + /// **'Liquidità disponibile'** + String get availableLiquidity; + + /// No description provided for @vsLastMonth. + /// + /// In it, this message translates to: + /// **'VS mese scorso'** + String get vsLastMonth; + + /// No description provided for @monthlyBalance. + /// + /// In it, this message translates to: + /// **'Bilancio mensile'** + String get monthlyBalance; + + /// No description provided for @currentMonth. + /// + /// In it, this message translates to: + /// **'Mese corrente'** + String get currentMonth; + + /// No description provided for @lastMonth. + /// + /// In it, this message translates to: + /// **'Mese scorso'** + String get lastMonth; + + /// No description provided for @yourAccounts. + /// + /// In it, this message translates to: + /// **'I tuoi conti'** + String get yourAccounts; + + /// No description provided for @yourBudgets. + /// + /// In it, this message translates to: + /// **'I tuoi budget'** + String get yourBudgets; + + /// No description provided for @createBudgetToTrack. + /// + /// In it, this message translates to: + /// **'Crea un budget per monitorare le tue spese'** + String get createBudgetToTrack; + + /// No description provided for @close. + /// + /// In it, this message translates to: + /// **'Chiudi'** + String get close; + + /// No description provided for @edit. + /// + /// In it, this message translates to: + /// **'Modifica'** + String get edit; + + /// No description provided for @errorDuplicatingTransaction. + /// + /// In it, this message translates to: + /// **'Errore durante la duplicazione della transazione'** + String get errorDuplicatingTransaction; + + /// No description provided for @transactionCreated. + /// + /// In it, this message translates to: + /// **'\"{transaction}\" è stata creata'** + String transactionCreated(Object transaction); + + /// No description provided for @left. + /// + /// In it, this message translates to: + /// **'Rimanenti'** + String get left; + + /// No description provided for @notEnoughDataForGraph. + /// + /// In it, this message translates to: + /// **'Siamo spiacenti, ma non ci sono\nabbastanza dati per creare il grafico...'** + String get notEnoughDataForGraph; + + /// No description provided for @generalSettingsDesc. + /// + /// In it, this message translates to: + /// **'Modifica impostazioni generali'** + String get generalSettingsDesc; + + /// No description provided for @accountsDesc. + /// + /// In it, this message translates to: + /// **'Aggiungi o modifica i tuoi conti'** + String get accountsDesc; + + /// No description provided for @categoriesDesc. + /// + /// In it, this message translates to: + /// **'Aggiungi/modifica categorie e sottocategorie'** + String get categoriesDesc; + + /// No description provided for @budget. + /// + /// In it, this message translates to: + /// **'Budget'** + String get budget; + + /// No description provided for @budgetDesc. + /// + /// In it, this message translates to: + /// **'Aggiungi o modifica i tuoi budget'** + String get budgetDesc; + + /// No description provided for @importExportDesc. + /// + /// In it, this message translates to: + /// **'Importa o esporta dati'** + String get importExportDesc; + + /// No description provided for @notificationsDesc. + /// + /// In it, this message translates to: + /// **'Gestisci le impostazioni delle notifiche'** + String get notificationsDesc; + + /// No description provided for @leaveFeedback. + /// + /// In it, this message translates to: + /// **'Lascia un feedback'** + String get leaveFeedback; + + /// No description provided for @leaveFeedbackDesc. + /// + /// In it, this message translates to: + /// **'Compila un modulo per segnalare un bug o lasciare un feedback'** + String get leaveFeedbackDesc; + + /// No description provided for @appInfoDesc. + /// + /// In it, this message translates to: + /// **'Scopri di più su di noi e sull\'app'** + String get appInfoDesc; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => + ['en', 'it', 'pt'].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(); + case 'it': + return AppLocalizationsIt(); + case 'pt': + return AppLocalizationsPt(); + } + + 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/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart new file mode 100644 index 00000000..001126df --- /dev/null +++ b/lib/l10n/app_localizations_en.dart @@ -0,0 +1,777 @@ +// 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 appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transactions'; + + @override + String get planning => 'Planning'; + + @override + String get graphs => 'Graphs'; + + @override + String get list => 'List'; + + @override + String get categories => 'Categories'; + + @override + String get expenses => 'Expenses'; + + @override + String get incomes => 'Incomes'; + + @override + String get expense => 'Expense'; + + @override + String get income => 'Income'; + + @override + String get transfer => 'Transfer'; + + @override + String get accounts => 'Accounts'; + + @override + String get details => 'Details'; + + @override + String get account => 'Account'; + + @override + String get category => 'Category'; + + @override + String get date => 'Date'; + + @override + String get investments => 'Investments'; + + @override + String get settings => 'Settings'; + + @override + String get notifications => 'Notifications'; + + @override + String get settingsDisclaimer => 'Open source, built by the community'; + + @override + String get addTransaction => 'Add transaction'; + + @override + String get totalBalance => 'Total balance'; + + @override + String get netWorth => 'Net worth'; + + @override + String get save => 'Save'; + + @override + String get cancel => 'Cancel'; + + @override + String get success => 'Success'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Editing transaction'; + + @override + String get newTransaction => 'New transaction'; + + @override + String get updateTransaction => 'Update transaction'; + + @override + String get recurringPayments => 'Recurring payments'; + + @override + String get interval => 'Interval'; + + @override + String get endRepetition => 'End repetition'; + + @override + String get never => 'Never'; + + @override + String get onADate => 'On a date'; + + @override + String get switchDisabled => 'Switch is disabled'; + + @override + String saveCsvFileFailed(Object e) { + return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + } + + @override + String errorPickingFile(Object error) { + return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + } + + @override + String get storagePermissionRequired => + 'Storage permission is required to access your files.'; + + @override + String get importingData => 'Importing data...'; + + @override + String get exportingData => 'Exporting data...'; + + @override + String fileSavedTo(Object path) { + return 'File saved to: $path'; + } + + @override + String get dataImportedSuccessfully => 'Data imported successfully'; + + @override + String get description => 'Description'; + + @override + String get addDescription => 'Add description'; + + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + + @override + String get duplicateTransactionTitle => 'Duplicate transaction'; + + @override + String get duplicateTransactionContent => + 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + + @override + String get duplicate => 'Duplicate'; + + @override + String get moreFrequent => 'More frequent'; + + @override + String get allCategories => 'All categories'; + + @override + String get allAccounts => 'All accounts'; + + @override + String errorOccurred(Object err) { + return 'Error: $err'; + } + + @override + String get selectAccount => 'Select Account'; + + @override + String get to => 'To:'; + + @override + String get from => 'From:'; + + @override + String get recurringTransactionAdded => 'Recurring transaction added'; + + @override + String get recurringTransactions => 'Recurring transactions'; + + @override + String get addTransactionReminder => 'Add transaction reminder'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get privacyCollectTitle => 'What Information Do We Collect?'; + + @override + String get privacyChangesTitle => 'Changes to This Privacy Policy'; + + @override + String get contactUsTitle => 'Contact us'; + + @override + String get privacyIntro => + 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + + @override + String get privacyCollectBody => + 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + + @override + String get privacyChangesBody => + 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + + @override + String get contactUsBody => + 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + + @override + String get collaboratorsTitle => 'Collaborators'; + + @override + String get meetTheTeam => 'Meet the team'; + + @override + String get teamDescription => + 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + + @override + String get wantToContribute => 'Want to contribute?'; + + @override + String get contributeDescription => + 'Open an issue, submit a PR or just say hi on GitHub'; + + @override + String get appInfo => 'App Info'; + + @override + String get appVersion => 'App Version:'; + + @override + String get collaborators => 'Collaborators'; + + @override + String get collaboratorsDescription => 'See the team behind this app'; + + @override + String get privacyPolicy => 'Privacy Policy'; + + @override + String get privacyPolicyDescription => 'Read more'; + + @override + String get generalSettings => 'General Settings'; + + @override + String get appearance => 'Appearance'; + + @override + String get currency => 'Currency'; + + @override + String get requireAuthentication => 'Require authentication'; + + @override + String get searchForATransaction => 'Search for a transaction'; + + @override + String get selectACurrency => 'Select a currency'; + + @override + String get search => 'Search'; + + @override + String get searchIn => 'Search in'; + + @override + String get lastTransactions => 'Your last transactions'; + + @override + String get startReconciliation => 'Start reconciliation'; + + @override + String get newBalance => 'New balance'; + + @override + String get balanceDiscrepancy => 'Balance Discrepancy?'; + + @override + String get balanceAdjustmentHint => + 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + + @override + String get newAccount => 'New account'; + + @override + String get editAccount => 'Edit account'; + + @override + String get createAccount => 'Create account'; + + @override + String get accountName => 'Account name'; + + @override + String get name => 'Name'; + + @override + String get iconAndColor => 'Icon and color'; + + @override + String get chooseColor => 'Choose color'; + + @override + String get chooseIcon => 'Choose icon'; + + @override + String get done => 'Fatto'; + + @override + String get add => 'Add'; + + @override + String get setAsMainAccount => 'Set as main account'; + + @override + String get countsForNetWorth => 'Counts for the net worth'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get initialBalance => 'Initial balance'; + + @override + String get currentBalance => 'Current balance'; + + @override + String get showLess => 'Show less'; + + @override + String get showMore => 'Show more'; + + @override + String get addSubcategory => 'Add subcategory'; + + @override + String get newCategory => 'New category'; + + @override + String get editCategory => 'Edit category'; + + @override + String get createCategory => 'Create category'; + + @override + String get updateCategory => 'Update category'; + + @override + String get categoryName => 'Category name'; + + @override + String get type => 'Type'; + + @override + String get deleteCategory => 'Delete category'; + + @override + String get newSubcategory => 'New subcategory'; + + @override + String get editSubcategory => 'Edit subcategory'; + + @override + String get createSubcategory => 'Create subcategory'; + + @override + String get updateSubcategory => 'Update subcategory'; + + @override + String get subcategoryName => 'Subcategory name'; + + @override + String get deleteSubcategory => 'Delete subcategory'; + + @override + String get subcategory => 'Subcategory'; + + @override + String get categoryFirstThenBudget => 'Add a category first to set a budget'; + + @override + String inTheNextDays(Object next) { + return 'In $next days'; + } + + @override + String get monthlyBudget => 'Monthly budget'; + + @override + String get manage => 'Manage'; + + @override + String get swipeLeftToDelete => 'Swipe left to delete'; + + @override + String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + + @override + String get saveBudget => 'Save budget'; + + @override + String get selectCategoriesToCreateBudget => + 'Select the categories to create your budget'; + + @override + String get amount => 'Amount'; + + @override + String get addCategoryBudget => 'Add category budget'; + + @override + String get allCategoriesAdded => + 'You have already added all available categories.'; + + @override + String get delete => 'Delete'; + + @override + String get allRecurringPaymentsHere => + 'All recurring payments will be displayed here'; + + @override + String get addRecurringPayment => 'Add recurring payment'; + + @override + String get seeOlderPayments => 'See older payments'; + + @override + String untilDate(Object date) { + return 'Until $date'; + } + + @override + String get olderPayments => 'Older payments'; + + @override + String get categoryNotFound => 'Category not found'; + + @override + String get back => 'Back'; + + @override + String onTheDay(Object day) { + return '- On the $day day'; + } + + @override + String get noMonthlyPaymentHistory => 'No monthly payment history'; + + @override + String get noRecurrentPaymentHistory => 'No recurrent payment history'; + + @override + String errorLoadingPayments(Object error) { + return 'Error loading payments: $error'; + } + + @override + String get editRecurringTransaction => 'Edit recurring transaction'; + + @override + String get detailsExplanation => + 'Details (any change will affect only future transactions)'; + + @override + String get dateStart => 'Date start'; + + @override + String get planned => 'Planned'; + + @override + String get composition => 'Composition'; + + @override + String get progress => 'Progress'; + + @override + String get noBudgetSet => 'There are no budgets set'; + + @override + String get budgetHelpText => + 'A monthly budget can help you keep track of your expenses and stay within the limits'; + + @override + String get createBudget => 'Create budget'; + + @override + String get setUpTheApp => 'Set up the app'; + + @override + String get setupDescription => + 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + + @override + String get startTheSetup => 'Start the setup'; + + @override + String budgetAmount(Object amount) { + return 'Budget $amount€'; + } + + @override + String get addBudget => 'Add budget'; + + @override + String addBudgetForCategory(Object cat) { + return 'Add budget for category $cat'; + } + + @override + String get addCategory => 'Add category'; + + @override + String get confirm => 'Confirm'; + + @override + String get step1Of2 => 'Step 1 of 2'; + + @override + String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + + @override + String get chooseCategoriesForBudget => + 'Choose which categories you want to set a budget for'; + + @override + String get monthlyBudgetTotal => 'Monthly budget total:'; + + @override + String get nextStep => 'Next step'; + + @override + String get continueWithoutBudget => 'Continue without budget'; + + @override + String get step2Of2 => 'Step 2 OF 2'; + + @override + String get setLiquidityInMainAccount => + 'Set the liquidity in your main account'; + + @override + String get addMoreAccounts => + 'You\'ll be able to add more accounts within the app.'; + + @override + String get liquidityDescription => + 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + + @override + String get mainAccount => 'Main account'; + + @override + String get setAmount => 'Set amount'; + + @override + String get editIconAndColor => 'Edit icon and color'; + + @override + String get skipStepOrStartFromZero => + 'Or you can skip this step and start from 0'; + + @override + String get startTrackingExpenses => 'Start tracking your expenses'; + + @override + String get startFromZero => 'Start from 0'; + + @override + String get importExport => 'Import/Export'; + + @override + String get importData => 'Import data'; + + @override + String get importDataDescription => + 'Import a CSV file to update your database'; + + @override + String get importMoneyManager => 'Import from Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + + @override + String get exportData => 'Export data'; + + @override + String get exportDataDescription => 'Save your data as a CSV file'; + + @override + String get warningOverwrite => 'Warning: Data Overwrite'; + + @override + String get warningOverwriteContent => + 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + + @override + String get proceedImport => 'Proceed with Import'; + + @override + String get importSuccess => 'Data imported successfully'; + + @override + String exportFailed(Object err) { + return 'Export failed: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Failed to export table: $tableName'; + } + + @override + String get errorCsvNotFound => 'CSV file not found.'; + + @override + String get errorCsvEmpty => 'The CSV file is empty.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Missing expected column: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Found an unexpected value: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'A general error occurred during CSV import. With error: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Failed to import transaction on date: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Failed to clean the database. Reason: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Failed to reset the database. Reason: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transactions'; + } + + @override + String get uncategorized => 'Uncategorized'; + + @override + String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + + @override + String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + + @override + String get total => 'Total'; + + @override + String get noTransactionsAdded => 'There are no transactions added yet'; + + @override + String get addTransactionCallToAction => + 'Add a transaction to make this section more appealing'; + + @override + String get graphsEmptyState => + 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + + @override + String get availableLiquidity => 'Available liquidity'; + + @override + String get vsLastMonth => 'VS last month'; + + @override + String get monthlyBalance => 'Monthly balance'; + + @override + String get currentMonth => 'Current month'; + + @override + String get lastMonth => 'Last month'; + + @override + String get yourAccounts => 'Your accounts'; + + @override + String get yourBudgets => 'Your budgets'; + + @override + String get createBudgetToTrack => 'Create a budget to track your spending'; + + @override + String get close => 'Close'; + + @override + String get edit => 'Edit'; + + @override + String get errorDuplicatingTransaction => 'Error duplicating transaction'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" has been created'; + } + + @override + String get left => 'Left'; + + @override + String get notEnoughDataForGraph => + 'We are sorry but there is not\nenough data to make the graph...'; + + @override + String get generalSettingsDesc => 'Edit general settings'; + + @override + String get accountsDesc => 'Add or edit your accounts'; + + @override + String get categoriesDesc => 'Add/edit categories and subcategories'; + + @override + String get budget => 'Budget'; + + @override + String get budgetDesc => 'Add or edit your budgets'; + + @override + String get importExportDesc => 'Import or export data'; + + @override + String get notificationsDesc => 'Manage your notifications settings'; + + @override + String get leaveFeedback => 'Leave a feedback'; + + @override + String get leaveFeedbackDesc => + 'Complete a small form to report a bug or leave a feedback'; + + @override + String get appInfoDesc => 'Learn more about us and the app'; +} diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart new file mode 100644 index 00000000..03fdfa60 --- /dev/null +++ b/lib/l10n/app_localizations_it.dart @@ -0,0 +1,783 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Italian (`it`). +class AppLocalizationsIt extends AppLocalizations { + AppLocalizationsIt([String locale = 'it']) : super(locale); + + @override + String get appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transazioni'; + + @override + String get planning => 'Pianificazione'; + + @override + String get graphs => 'Grafici'; + + @override + String get list => 'Lista'; + + @override + String get categories => 'Categorie'; + + @override + String get expenses => 'Spese'; + + @override + String get incomes => 'Entrate'; + + @override + String get expense => 'Spesa'; + + @override + String get income => 'Entrata'; + + @override + String get transfer => 'Spostamento'; + + @override + String get accounts => 'Conti'; + + @override + String get details => 'Dettagli'; + + @override + String get account => 'Conto'; + + @override + String get category => 'Categoria'; + + @override + String get date => 'Data'; + + @override + String get investments => 'Investimenti'; + + @override + String get settings => 'Impostazioni'; + + @override + String get notifications => 'Notifiche'; + + @override + String get settingsDisclaimer => 'Open source, sviluppata dalla community'; + + @override + String get addTransaction => 'Aggiungi transazione'; + + @override + String get totalBalance => 'Saldo Totale'; + + @override + String get netWorth => 'Patrimonio Netto'; + + @override + String get save => 'Salva'; + + @override + String get cancel => 'Annulla'; + + @override + String get success => 'Successo'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Modifica transazione'; + + @override + String get newTransaction => 'Nuova transazione'; + + @override + String get updateTransaction => 'Modifica transazione'; + + @override + String get recurringPayments => 'Pagamenti ricorrenti'; + + @override + String get interval => 'Intervallo'; + + @override + String get endRepetition => 'Fine ripetizione'; + + @override + String get never => 'Mai'; + + @override + String get onADate => 'In data'; + + @override + String get switchDisabled => 'Gesto disabilitato'; + + @override + String saveCsvFileFailed(Object e) { + return 'Non puoi salvare i file qui, crea o seleziona una cartella in Downloads o Documenti. Errore: \$$e'; + } + + @override + String errorPickingFile(Object error) { + return 'Errore durante la selezione del file. Assicurati di avere i permessi necessari. Errore: $error'; + } + + @override + String get storagePermissionRequired => + 'È richiesto il permesso di archiviazione per accedere ai file.'; + + @override + String get importingData => 'Importazione dati in corso...'; + + @override + String get exportingData => 'Esportazione dati in corso...'; + + @override + String fileSavedTo(Object path) { + return 'File salvato in: $path'; + } + + @override + String get dataImportedSuccessfully => 'Dati importati con successo'; + + @override + String get description => 'Descrizione'; + + @override + String get addDescription => 'Aggiungi descrizione'; + + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + + @override + String get duplicateTransactionTitle => 'Transazione duplicata'; + + @override + String get duplicateTransactionContent => + 'Questa transazione è già presente nella lista. Vuoi duplicarla? Potrai poi modificare il nuovo inserimento.'; + + @override + String get duplicate => 'Duplica'; + + @override + String get moreFrequent => 'Più frequente'; + + @override + String get allCategories => 'Tutte le categorie'; + + @override + String get allAccounts => 'Tutte i conti'; + + @override + String errorOccurred(Object err) { + return 'Errore: $err'; + } + + @override + String get selectAccount => 'Seleziona conto'; + + @override + String get to => 'A:'; + + @override + String get from => 'Da:'; + + @override + String get recurringTransactionAdded => 'Transazione ricorrente aggiunta'; + + @override + String get recurringTransactions => 'Transazioni ricorrenti'; + + @override + String get addTransactionReminder => 'Aggiungi promemoria transazione'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get privacyCollectTitle => 'Quali informazioni raccogliamo?'; + + @override + String get privacyChangesTitle => 'Modifiche alla Privacy Policy'; + + @override + String get contactUsTitle => 'Contattaci'; + + @override + String get privacyIntro => + 'Sossoldi è sviluppata come un\'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'; + + @override + String get privacyCollectBody => + 'Sossoldi non raccoglie alcuna informazione personale e non si connette a Internet. Qualsiasi informazione aggiunta nell\'app esiste esclusivamente sul tuo dispositivo e da nessun\'altra parte.\n'; + + @override + String get privacyChangesBody => + 'Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n'; + + @override + String get contactUsBody => + 'Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all\'indirizzo\n'; + + @override + String get collaboratorsTitle => 'Collaboratori'; + + @override + String get meetTheTeam => 'Incontra il team'; + + @override + String get teamDescription => + 'Sossoldi è sviluppata e mantenuta da una appassionata community open source. Ogni funzione, correzione e idea arriva da persone come te.'; + + @override + String get wantToContribute => 'Vuoi contribuire?'; + + @override + String get contributeDescription => + 'Apri una issue, invia una PR o semplicemente saluta su GitHub'; + + @override + String get appInfo => 'Informazioni app'; + + @override + String get appVersion => 'Versione app:'; + + @override + String get collaborators => 'Collaboratori'; + + @override + String get collaboratorsDescription => 'Scopri il team dietro questa app'; + + @override + String get privacyPolicy => 'Privacy Policy'; + + @override + String get privacyPolicyDescription => 'Leggi di più'; + + @override + String get generalSettings => 'Impostazioni generali'; + + @override + String get appearance => 'Aspetto'; + + @override + String get currency => 'Valuta'; + + @override + String get requireAuthentication => 'Richiedi autenticazione'; + + @override + String get searchForATransaction => 'Cerca una transazione'; + + @override + String get selectACurrency => 'Seleziona una valuta'; + + @override + String get search => 'Cerca'; + + @override + String get searchIn => 'Cerca in'; + + @override + String get lastTransactions => 'Le tue ultime transazioni'; + + @override + String get startReconciliation => 'Avvia riconciliazione'; + + @override + String get newBalance => 'Nuovo saldo'; + + @override + String get balanceDiscrepancy => 'Differenza di saldo?'; + + @override + String get balanceAdjustmentHint => + 'Il saldo registrato potrebbe differire dall\'estratto conto della tua banca. Tocca qui sotto per regolare manualmente il saldo e mantenere i tuoi registri aggiornati.'; + + @override + String get newAccount => 'Nuovo conto'; + + @override + String get editAccount => 'Modifica conto'; + + @override + String get createAccount => 'Crea conto'; + + @override + String get accountName => 'Nome del conto'; + + @override + String get name => 'Nome'; + + @override + String get iconAndColor => 'Icona e colore'; + + @override + String get chooseColor => 'Scegli colore'; + + @override + String get chooseIcon => 'Scegli icona'; + + @override + String get done => 'Fatto'; + + @override + String get add => 'Aggiungi'; + + @override + String get setAsMainAccount => 'Imposta come conto principale'; + + @override + String get countsForNetWorth => 'Includi nel patrimonio netto'; + + @override + String get deleteAccount => 'Elimina conto'; + + @override + String get initialBalance => 'Saldo iniziale'; + + @override + String get currentBalance => 'Saldo attuale'; + + @override + String get showLess => 'Mostra meno'; + + @override + String get showMore => 'Mostra altro'; + + @override + String get addSubcategory => 'Aggiungi sottocategoria'; + + @override + String get newCategory => 'Nuova categoria'; + + @override + String get editCategory => 'Modifica categoria'; + + @override + String get createCategory => 'Crea categoria'; + + @override + String get updateCategory => 'Aggiorna categoria'; + + @override + String get categoryName => 'Nome della categoria'; + + @override + String get type => 'Tipo'; + + @override + String get deleteCategory => 'Elimina categoria'; + + @override + String get newSubcategory => 'Nuova sottocategoria'; + + @override + String get editSubcategory => 'Modifica sottocategoria'; + + @override + String get createSubcategory => 'Crea sottocategoria'; + + @override + String get updateSubcategory => 'Aggiorna sottocategoria'; + + @override + String get subcategoryName => 'Nome della sottocategoria'; + + @override + String get deleteSubcategory => 'Elimina sottocategoria'; + + @override + String get subcategory => 'Sottocagegoria'; + + @override + String get categoryFirstThenBudget => + 'Aggiungi una categoria prima di creare un budget'; + + @override + String inTheNextDays(Object next) { + return 'In $next giorni'; + } + + @override + String get monthlyBudget => 'Budget mensile'; + + @override + String get manage => 'Gestisci'; + + @override + String get swipeLeftToDelete => 'Scorri a sinistra per eliminare'; + + @override + String get yourMonthlyBudgetWillBe => 'Il tuo budget mensile sarà:'; + + @override + String get saveBudget => 'Salva budget'; + + @override + String get selectCategoriesToCreateBudget => + 'Seleziona le categorie per creare il tuo budget'; + + @override + String get amount => 'Importo'; + + @override + String get addCategoryBudget => 'Aggiungi budget categoria'; + + @override + String get allCategoriesAdded => + 'Hai già aggiunto tutte le categorie disponibili.'; + + @override + String get delete => 'Elimina'; + + @override + String get allRecurringPaymentsHere => + 'Tutti i pagamenti ricorrenti verranno visualizzati qui'; + + @override + String get addRecurringPayment => 'Aggiungi pagamento ricorrente'; + + @override + String get seeOlderPayments => 'Vedi pagamenti passati'; + + @override + String untilDate(Object date) { + return 'Fino al $date'; + } + + @override + String get olderPayments => 'Pagamenti passati'; + + @override + String get categoryNotFound => 'Categoria non trovata'; + + @override + String get back => 'Indietro'; + + @override + String onTheDay(Object day) { + return '- Il giorno $day'; + } + + @override + String get noMonthlyPaymentHistory => 'Nessuna cronologia pagamenti mensili'; + + @override + String get noRecurrentPaymentHistory => + 'Nessuna cronologia pagamenti ricorrenti'; + + @override + String errorLoadingPayments(Object error) { + return 'Errore nel caricamento pagamenti: $error'; + } + + @override + String get editRecurringTransaction => 'Modifica transazione ricorrente'; + + @override + String get detailsExplanation => + 'Dettagli (ogni modifica influenzerà solo le transazioni future)'; + + @override + String get dateStart => 'Data inizio'; + + @override + String get planned => 'Pianificato'; + + @override + String get composition => 'Composizione'; + + @override + String get progress => 'Avanzamento'; + + @override + String get noBudgetSet => 'Non ci sono budget impostati'; + + @override + String get budgetHelpText => + 'Un budget mensile può aiutarti a tenere traccia delle tue spese e a rimanere entro i limiti'; + + @override + String get createBudget => 'Crea budget'; + + @override + String get setUpTheApp => 'Configura l\'app'; + + @override + String get setupDescription => + 'In pochi passaggi sarai pronto a iniziare a tenere\ntraccia delle tue finanze personali (quasi) come\nMr. Rip.'; + + @override + String get startTheSetup => 'Inizia la configurazione'; + + @override + String budgetAmount(Object amount) { + return 'Budget $amount€'; + } + + @override + String get addBudget => 'Aggiungi budget'; + + @override + String addBudgetForCategory(Object cat) { + return 'Aggiungi un budget per la categoria $cat'; + } + + @override + String get addCategory => 'Aggiungi categoria'; + + @override + String get confirm => 'Conferma'; + + @override + String get step1Of2 => 'Passaggio 1 di 2'; + + @override + String get setupMonthlyBudgets => 'Imposta i tuoi budget\nmensili'; + + @override + String get chooseCategoriesForBudget => + 'Scegli le categorie per le quali vuoi impostare un budget'; + + @override + String get monthlyBudgetTotal => 'Totale budget mensile:'; + + @override + String get nextStep => 'Passaggio successivo'; + + @override + String get continueWithoutBudget => 'Continua senza budget'; + + @override + String get step2Of2 => 'Passaggio 2 DI 2'; + + @override + String get setLiquidityInMainAccount => + 'Imposta la liquidità nel tuo conto principale'; + + @override + String get addMoreAccounts => + 'Sarai in grado di aggiungere altri account dall\'app'; + + @override + String get liquidityDescription => + 'Verrà utilizzata come base a cui aggiungere entrate, spese e calcolare il tuo patrimonio.\nPotrai aggiungere altri conti all\'interno dell\'app.'; + + @override + String get mainAccount => 'Conto principale'; + + @override + String get setAmount => 'Imposta importo'; + + @override + String get editIconAndColor => 'Modifica icona e colore'; + + @override + String get skipStepOrStartFromZero => + 'Oppure puoi saltare questo passaggio e iniziare da 0'; + + @override + String get startTrackingExpenses => 'Inizia a tracciare le tue spese'; + + @override + String get startFromZero => 'Inizia da 0'; + + @override + String get importExport => 'Importa/Esporta'; + + @override + String get importData => 'Importa dati'; + + @override + String get importDataDescription => + 'Importa un file CSV per aggiornare il database'; + + @override + String get importMoneyManager => 'Importa da Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Importa CSV da Money Manager per aggiornare il database. Il file deve essere salvato in formato CSV da XLS.'; + + @override + String get exportData => 'Esporta dati'; + + @override + String get exportDataDescription => 'Salva i tuoi dati come file CSV'; + + @override + String get warningOverwrite => 'Attenzione: Sovrascrittura dati'; + + @override + String get warningOverwriteContent => + 'L\'importazione di questo file sostituirà definitivamente i tuoi dati esistenti. Questa azione non può essere annullata. Assicurati di avere un backup prima di procedere.'; + + @override + String get proceedImport => 'Procedi con l\'importazione'; + + @override + String get importSuccess => 'Dati importati con successo'; + + @override + String exportFailed(Object err) { + return 'Esportazione fallita: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Impossibile esportare la tabella: $tableName'; + } + + @override + String get errorCsvNotFound => 'File CSV non trovato.'; + + @override + String get errorCsvEmpty => 'Il file CSV è vuoto.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Colonna mancante nel CSV: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Valore non previsto trovato: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'Si è verificato un errore generale durante l\'importazione del CSV. Errore: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Errore durante l\'importazione della transazione in data: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Impossibile pulire il database. Motivo: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Impossibile ripristinare il database. Motivo: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transazioni'; + } + + @override + String get uncategorized => 'Senza categoria'; + + @override + String get noIncomesForSelectedMonth => + 'Nessuna entrata per il mese selezionato'; + + @override + String get noExpensesForSelectedMonth => + 'Nessuna spesa per il mese selezionato'; + + @override + String get total => 'Totale'; + + @override + String get noTransactionsAdded => 'Non ci sono ancora transazioni aggiunte'; + + @override + String get addTransactionCallToAction => + 'Aggiungi una transazione per rendere questa sezione più interessante'; + + @override + String get graphsEmptyState => + 'Dopo aver aggiunto alcune transazioni, dei grafici eccezionali appariranno qui... quasi per magia!'; + + @override + String get availableLiquidity => 'Liquidità disponibile'; + + @override + String get vsLastMonth => 'VS mese scorso'; + + @override + String get monthlyBalance => 'Bilancio mensile'; + + @override + String get currentMonth => 'Mese corrente'; + + @override + String get lastMonth => 'Mese scorso'; + + @override + String get yourAccounts => 'I tuoi conti'; + + @override + String get yourBudgets => 'I tuoi budget'; + + @override + String get createBudgetToTrack => + 'Crea un budget per monitorare le tue spese'; + + @override + String get close => 'Chiudi'; + + @override + String get edit => 'Modifica'; + + @override + String get errorDuplicatingTransaction => + 'Errore durante la duplicazione della transazione'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" è stata creata'; + } + + @override + String get left => 'Rimanenti'; + + @override + String get notEnoughDataForGraph => + 'Siamo spiacenti, ma non ci sono\nabbastanza dati per creare il grafico...'; + + @override + String get generalSettingsDesc => 'Modifica impostazioni generali'; + + @override + String get accountsDesc => 'Aggiungi o modifica i tuoi conti'; + + @override + String get categoriesDesc => 'Aggiungi/modifica categorie e sottocategorie'; + + @override + String get budget => 'Budget'; + + @override + String get budgetDesc => 'Aggiungi o modifica i tuoi budget'; + + @override + String get importExportDesc => 'Importa o esporta dati'; + + @override + String get notificationsDesc => 'Gestisci le impostazioni delle notifiche'; + + @override + String get leaveFeedback => 'Lascia un feedback'; + + @override + String get leaveFeedbackDesc => + 'Compila un modulo per segnalare un bug o lasciare un feedback'; + + @override + String get appInfoDesc => 'Scopri di più su di noi e sull\'app'; +} diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart new file mode 100644 index 00000000..ceef5941 --- /dev/null +++ b/lib/l10n/app_localizations_pt.dart @@ -0,0 +1,782 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Portuguese (`pt`). +class AppLocalizationsPt extends AppLocalizations { + AppLocalizationsPt([String locale = 'pt']) : super(locale); + + @override + String get appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transações'; + + @override + String get planning => 'Planejamento'; + + @override + String get graphs => 'Gráficos'; + + @override + String get list => 'Lista'; + + @override + String get categories => 'Categorias'; + + @override + String get expenses => 'Despesas'; + + @override + String get incomes => 'Receitas'; + + @override + String get expense => 'Despesa'; + + @override + String get income => 'Receita'; + + @override + String get transfer => 'Transferência'; + + @override + String get accounts => 'Contas'; + + @override + String get details => 'Detalhes'; + + @override + String get account => 'Conta'; + + @override + String get category => 'Categoria'; + + @override + String get date => 'Data'; + + @override + String get investments => 'Investimentos'; + + @override + String get settings => 'Configurações'; + + @override + String get notifications => 'Notificações'; + + @override + String get settingsDisclaimer => 'Open source, desenvolvida pela comunidade'; + + @override + String get addTransaction => 'Adicionar transação'; + + @override + String get totalBalance => 'Saldo Total'; + + @override + String get netWorth => 'Patrimônio Líquido'; + + @override + String get save => 'Salvar'; + + @override + String get cancel => 'Cancelar'; + + @override + String get success => 'Sucesso'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Editar transação'; + + @override + String get newTransaction => 'Nova transação'; + + @override + String get updateTransaction => 'Atualizar transação'; + + @override + String get recurringPayments => 'Pagamentos recorrentes'; + + @override + String get interval => 'Intervalo'; + + @override + String get endRepetition => 'Fim da repetição'; + + @override + String get never => 'Nunca'; + + @override + String get onADate => 'Em uma data'; + + @override + String get switchDisabled => 'Gesto desativado'; + + @override + String saveCsvFileFailed(Object e) { + return 'Não é possível salvar arquivos aqui, crie ou selecione uma pasta em Downloads ou Documentos. Erro: \$$e'; + } + + @override + String errorPickingFile(Object error) { + return 'Erro ao selecionar o arquivo. Certifique-se de ter as permissões necessárias. Erro: $error'; + } + + @override + String get storagePermissionRequired => + 'É necessária permissão de armazenamento para acessar os arquivos.'; + + @override + String get importingData => 'Importando dados...'; + + @override + String get exportingData => 'Exportando dados...'; + + @override + String fileSavedTo(Object path) { + return 'Arquivo salvo em: $path'; + } + + @override + String get dataImportedSuccessfully => 'Dados importados com sucesso'; + + @override + String get description => 'Descrição'; + + @override + String get addDescription => 'Adicionar descrição'; + + @override + String get recurringTransactionWarning => + 'Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.'; + + @override + String get duplicateTransactionTitle => 'Duplicar transação'; + + @override + String get duplicateTransactionContent => + 'Esta transação já está na lista. Deseja duplicá-la? Você poderá editar a nova entrada posteriormente.'; + + @override + String get duplicate => 'Duplicar'; + + @override + String get moreFrequent => 'Mais frequente'; + + @override + String get allCategories => 'Todas as categorias'; + + @override + String get allAccounts => 'Todas as contas'; + + @override + String errorOccurred(Object err) { + return 'Erro: $err'; + } + + @override + String get selectAccount => 'Selecionar conta'; + + @override + String get to => 'Para:'; + + @override + String get from => 'De:'; + + @override + String get recurringTransactionAdded => 'Transação recorrente adicionada'; + + @override + String get recurringTransactions => 'Transações recorrentes'; + + @override + String get addTransactionReminder => 'Adicionar lembrete de transação'; + + @override + String get privacyPolicyTitle => 'Política de Privacidade'; + + @override + String get privacyCollectTitle => 'Quais informações coletamos?'; + + @override + String get privacyChangesTitle => 'Alterações na Política de Privacidade'; + + @override + String get contactUsTitle => 'Contate-nos'; + + @override + String get privacyIntro => + 'O Sossoldi é desenvolvido como um aplicativo open source. Este serviço é fornecido gratuitamente e destina-se a ser usado como está.\nNão temos interesse em coletar nenhuma informação pessoal. Acreditamos que essas informações pertencem apenas a você. Não armazenamos nem transmitimos seus dados pessoais, nem incluímos software de publicidade ou análise que se comunique com terceiros.\n'; + + @override + String get privacyCollectBody => + 'O Sossoldi não coleta nenhuma informação pessoal e não se conecta à Internet. Qualquer informação adicionada ao aplicativo existe exclusivamente no seu dispositivo e em nenhum outro lugar.\n'; + + @override + String get privacyChangesBody => + 'Podemos atualizar nossa Política de Privacidade ocasionalmente. Portanto, recomendamos que você revise esta página periodicamente para verificar alterações.\nEsta política entra em vigor a partir de 01/01/2024.\n'; + + @override + String get contactUsBody => + 'Se você tiver dúvidas ou sugestões sobre nossa Política de Privacidade, não hesite em nos contatar em\n'; + + @override + String get collaboratorsTitle => 'Colaboradores'; + + @override + String get meetTheTeam => 'Conheça a equipe'; + + @override + String get teamDescription => + 'O Sossoldi é desenvolvido e mantido por uma apaixonada comunidade open source. Cada funcionalidade, correção e ideia vem de pessoas como você.'; + + @override + String get wantToContribute => 'Quer contribuir?'; + + @override + String get contributeDescription => + 'Abra uma issue, envie um PR ou apenas diga olá no GitHub'; + + @override + String get appInfo => 'Informações do app'; + + @override + String get appVersion => 'Versão do app:'; + + @override + String get collaborators => 'Colaboradores'; + + @override + String get collaboratorsDescription => 'Conheça a equipe por trás deste app'; + + @override + String get privacyPolicy => 'Política de Privacidade'; + + @override + String get privacyPolicyDescription => 'Saiba mais'; + + @override + String get generalSettings => 'Configurações gerais'; + + @override + String get appearance => 'Aparência'; + + @override + String get currency => 'Moeda'; + + @override + String get requireAuthentication => 'Requerer autenticação'; + + @override + String get searchForATransaction => 'Buscar uma transação'; + + @override + String get selectACurrency => 'Selecionar uma moeda'; + + @override + String get search => 'Buscar'; + + @override + String get searchIn => 'Buscar em'; + + @override + String get lastTransactions => 'Suas últimas transações'; + + @override + String get startReconciliation => 'Iniciar reconciliação'; + + @override + String get newBalance => 'Novo saldo'; + + @override + String get balanceDiscrepancy => 'Diferença de saldo?'; + + @override + String get balanceAdjustmentHint => + 'O saldo registrado pode diferir do extrato bancário. Toque abaixo para ajustar o saldo manualmente e manter seus registros atualizados.'; + + @override + String get newAccount => 'Nova conta'; + + @override + String get editAccount => 'Editar conta'; + + @override + String get createAccount => 'Criar conta'; + + @override + String get accountName => 'Nome da conta'; + + @override + String get name => 'Nome'; + + @override + String get iconAndColor => 'Ícone e cor'; + + @override + String get chooseColor => 'Escolher cor'; + + @override + String get chooseIcon => 'Escolher ícone'; + + @override + String get done => 'Feito'; + + @override + String get add => 'Adicionar'; + + @override + String get setAsMainAccount => 'Definir como conta principal'; + + @override + String get countsForNetWorth => 'Incluir no patrimônio líquido'; + + @override + String get deleteAccount => 'Excluir conta'; + + @override + String get initialBalance => 'Saldo inicial'; + + @override + String get currentBalance => 'Saldo atual'; + + @override + String get showLess => 'Mostrar menos'; + + @override + String get showMore => 'Mostrar mais'; + + @override + String get addSubcategory => 'Adicionar subcategoria'; + + @override + String get newCategory => 'Nova categoria'; + + @override + String get editCategory => 'Editar categoria'; + + @override + String get createCategory => 'Criar categoria'; + + @override + String get updateCategory => 'Atualizar categoria'; + + @override + String get categoryName => 'Nome da categoria'; + + @override + String get type => 'Tipo'; + + @override + String get deleteCategory => 'Excluir categoria'; + + @override + String get newSubcategory => 'Nova subcategoria'; + + @override + String get editSubcategory => 'Editar subcategoria'; + + @override + String get createSubcategory => 'Criar subcategoria'; + + @override + String get updateSubcategory => 'Atualizar subcategoria'; + + @override + String get subcategoryName => 'Nome da subcategoria'; + + @override + String get deleteSubcategory => 'Excluir subcategoria'; + + @override + String get subcategory => 'Subcategoria'; + + @override + String get categoryFirstThenBudget => + 'Adicione uma categoria antes de criar um orçamento'; + + @override + String inTheNextDays(Object next) { + return 'Nos próximos $next dias'; + } + + @override + String get monthlyBudget => 'Orçamento mensal'; + + @override + String get manage => 'Gerenciar'; + + @override + String get swipeLeftToDelete => 'Deslize para a esquerda para excluir'; + + @override + String get yourMonthlyBudgetWillBe => 'Seu orçamento mensal será:'; + + @override + String get saveBudget => 'Salvar orçamento'; + + @override + String get selectCategoriesToCreateBudget => + 'Selecione as categorias para criar seu orçamento'; + + @override + String get amount => 'Valor'; + + @override + String get addCategoryBudget => 'Adicionar orçamento à categoria'; + + @override + String get allCategoriesAdded => + 'Você já adicionou todas as categorias disponíveis.'; + + @override + String get delete => 'Excluir'; + + @override + String get allRecurringPaymentsHere => + 'Todos os pagamentos recorrentes serão exibidos aqui'; + + @override + String get addRecurringPayment => 'Adicionar pagamento recorrente'; + + @override + String get seeOlderPayments => 'Ver pagamentos antigos'; + + @override + String untilDate(Object date) { + return 'Até $date'; + } + + @override + String get olderPayments => 'Pagamentos antigos'; + + @override + String get categoryNotFound => 'Categoria não encontrada'; + + @override + String get back => 'Voltar'; + + @override + String onTheDay(Object day) { + return '- No dia $day'; + } + + @override + String get noMonthlyPaymentHistory => 'Sem histórico de pagamentos mensais'; + + @override + String get noRecurrentPaymentHistory => + 'Sem histórico de pagamentos recorrentes'; + + @override + String errorLoadingPayments(Object error) { + return 'Erro ao carregar pagamentos: $error'; + } + + @override + String get editRecurringTransaction => 'Editar transação recorrente'; + + @override + String get detailsExplanation => + 'Detalhes (qualquer alteração afetará apenas transações futuras)'; + + @override + String get dateStart => 'Data de início'; + + @override + String get planned => 'Planejado'; + + @override + String get composition => 'Composição'; + + @override + String get progress => 'Progresso'; + + @override + String get noBudgetSet => 'Nenhum orçamento definido'; + + @override + String get budgetHelpText => + 'Um orçamento mensal pode ajudá-lo a acompanhar suas despesas e manter-se dentro dos limites'; + + @override + String get createBudget => 'Criar orçamento'; + + @override + String get setUpTheApp => 'Configurar o app'; + + @override + String get setupDescription => + 'Em alguns passos você estará pronto para começar a\nacompanhar suas finanças pessoais (quase)\ncomo o Mr. Rip.'; + + @override + String get startTheSetup => 'Iniciar configuração'; + + @override + String budgetAmount(Object amount) { + return 'Orçamento $amount€'; + } + + @override + String get addBudget => 'Adicionar orçamento'; + + @override + String addBudgetForCategory(Object cat) { + return 'Adicionar orçamento para a categoria $cat'; + } + + @override + String get addCategory => 'Adicionar categoria'; + + @override + String get confirm => 'Confirmar'; + + @override + String get step1Of2 => 'Passo 1 de 2'; + + @override + String get setupMonthlyBudgets => 'Defina seus orçamentos\nmensais'; + + @override + String get chooseCategoriesForBudget => + 'Escolha as categorias para as quais deseja definir um orçamento'; + + @override + String get monthlyBudgetTotal => 'Total do orçamento mensal:'; + + @override + String get nextStep => 'Próximo passo'; + + @override + String get continueWithoutBudget => 'Continuar sem orçamento'; + + @override + String get step2Of2 => 'Passo 2 de 2'; + + @override + String get setLiquidityInMainAccount => + 'Defina a liquidez na sua conta principal'; + + @override + String get addMoreAccounts => + 'Você poderá adicionar mais contas dentro do app'; + + @override + String get liquidityDescription => + 'Ela será usada como base para adicionar receitas, despesas e calcular seu patrimônio.\nVocê poderá adicionar mais contas dentro do app.'; + + @override + String get mainAccount => 'Conta principal'; + + @override + String get setAmount => 'Definir valor'; + + @override + String get editIconAndColor => 'Editar ícone e cor'; + + @override + String get skipStepOrStartFromZero => + 'Ou você pode pular este passo e começar do 0'; + + @override + String get startTrackingExpenses => 'Começar a acompanhar suas despesas'; + + @override + String get startFromZero => 'Começar do 0'; + + @override + String get importExport => 'Importar/Exportar'; + + @override + String get importData => 'Importar dados'; + + @override + String get importDataDescription => + 'Importe um arquivo CSV para atualizar o banco de dados'; + + @override + String get importMoneyManager => 'Importar do Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Importe CSV do Money Manager para atualizar o banco de dados. O arquivo deve ser salvo em formato CSV a partir de XLS.'; + + @override + String get exportData => 'Exportar dados'; + + @override + String get exportDataDescription => 'Salve seus dados como um arquivo CSV'; + + @override + String get warningOverwrite => 'Atenção: Sobrescrita de dados'; + + @override + String get warningOverwriteContent => + 'A importação deste arquivo substituirá permanentemente seus dados existentes. Esta ação não pode ser desfeita. Certifique-se de ter um backup antes de prosseguir.'; + + @override + String get proceedImport => 'Prosseguir com a importação'; + + @override + String get importSuccess => 'Dados importados com sucesso'; + + @override + String exportFailed(Object err) { + return 'Falha na exportação: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Não foi possível exportar a tabela: $tableName'; + } + + @override + String get errorCsvNotFound => 'Arquivo CSV não encontrado.'; + + @override + String get errorCsvEmpty => 'O arquivo CSV está vazio.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Coluna faltando no CSV: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Valor inesperado encontrado: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'Ocorreu um erro geral durante a importação do CSV. Erro: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Erro ao importar a transação na data: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Não foi possível limpar o banco de dados. Motivo: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Não foi possível redefinir o banco de dados. Motivo: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transações'; + } + + @override + String get uncategorized => 'Sem categoria'; + + @override + String get noIncomesForSelectedMonth => + 'Nenhuma receita para o mês selecionado'; + + @override + String get noExpensesForSelectedMonth => + 'Nenhuma despesa para o mês selecionado'; + + @override + String get total => 'Total'; + + @override + String get noTransactionsAdded => 'Nenhuma transação adicionada ainda'; + + @override + String get addTransactionCallToAction => + 'Adicione uma transação para tornar esta seção mais interessante'; + + @override + String get graphsEmptyState => + 'Depois de adicionar algumas transações, gráficos incríveis aparecerão aqui... quase como mágica!'; + + @override + String get availableLiquidity => 'Liquidez disponível'; + + @override + String get vsLastMonth => 'VS mês anterior'; + + @override + String get monthlyBalance => 'Saldo mensal'; + + @override + String get currentMonth => 'Mês atual'; + + @override + String get lastMonth => 'Mês anterior'; + + @override + String get yourAccounts => 'Suas contas'; + + @override + String get yourBudgets => 'Seus orçamentos'; + + @override + String get createBudgetToTrack => + 'Crie um orçamento para acompanhar suas despesas'; + + @override + String get close => 'Fechar'; + + @override + String get edit => 'Editar'; + + @override + String get errorDuplicatingTransaction => 'Erro ao duplicar a transação'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" foi criada'; + } + + @override + String get left => 'Restante'; + + @override + String get notEnoughDataForGraph => + 'Lamentamos, mas não há\n-dados suficientes para criar o gráfico...'; + + @override + String get generalSettingsDesc => 'Editar configurações gerais'; + + @override + String get accountsDesc => 'Adicionar ou editar suas contas'; + + @override + String get categoriesDesc => 'Adicionar/editar categorias e subcategorias'; + + @override + String get budget => 'Orçamento'; + + @override + String get budgetDesc => 'Adicionar ou editar seus orçamentos'; + + @override + String get importExportDesc => 'Importar ou exportar dados'; + + @override + String get notificationsDesc => 'Gerenciar suas configurações de notificação'; + + @override + String get leaveFeedback => 'Deixar feedback'; + + @override + String get leaveFeedbackDesc => + 'Preencha um pequeno formulário para relatar um bug ou deixar feedback'; + + @override + String get appInfoDesc => 'Saiba mais sobre nós e o app'; +} diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb new file mode 100644 index 00000000..9e6fcc33 --- /dev/null +++ b/lib/l10n/app_pt.arb @@ -0,0 +1,240 @@ +{ + "@@locale": "pt", + "appName": "Sossoldi", + "@appName": { + "description": "O nome da aplicação" + }, + "dashboard": "Dashboard", + "transactions": "Transações", + "planning": "Planejamento", + "graphs": "Gráficos", + "list": "Lista", + "categories": "Categorias", + "expenses": "Despesas", + "incomes": "Receitas", + "expense": "Despesa", + "income": "Receita", + "transfer": "Transferência", + "accounts": "Contas", + "details": "Detalhes", + "account": "Conta", + "category": "Categoria", + "date": "Data", + "investments": "Investimentos", + "settings": "Configurações", + "notifications": "Notificações", + "settingsDisclaimer": "Open source, desenvolvida pela comunidade", + "addTransaction": "Adicionar transação", + "totalBalance": "Saldo Total", + "netWorth": "Patrimônio Líquido", + "save": "Salvar", + "cancel": "Cancelar", + "success": "Sucesso", + "ok": "Ok", + "editingTransaction": "Editar transação", + "newTransaction": "Nova transação", + "updateTransaction": "Atualizar transação", + "recurringPayments": "Pagamentos recorrentes", + "interval": "Intervalo", + "endRepetition": "Fim da repetição", + "never": "Nunca", + "onADate": "Em uma data", + "switchDisabled": "Gesto desativado", + "saveCsvFileFailed": "Não é possível salvar arquivos aqui, crie ou selecione uma pasta em Downloads ou Documentos. Erro: ${e}", + "errorPickingFile": "Erro ao selecionar o arquivo. Certifique-se de ter as permissões necessárias. Erro: {error}", + "storagePermissionRequired": "É necessária permissão de armazenamento para acessar os arquivos.", + "importingData": "Importando dados...", + "exportingData": "Exportando dados...", + "fileSavedTo": "Arquivo salvo em: {path}", + "dataImportedSuccessfully": "Dados importados com sucesso", + "description": "Descrição", + "addDescription": "Adicionar descrição", + "recurringTransactionWarning": "Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.", + "duplicateTransactionTitle": "Duplicar transação", + "duplicateTransactionContent": "Esta transação já está na lista. Deseja duplicá-la? Você poderá editar a nova entrada posteriormente.", + "duplicate": "Duplicar", + "moreFrequent": "Mais frequente", + "allCategories": "Todas as categorias", + "allAccounts": "Todas as contas", + "errorOccurred": "Erro: {err}", + "selectAccount": "Selecionar conta", + "to": "Para:", + "from": "De:", + "recurringTransactionAdded": "Transação recorrente adicionada", + "recurringTransactions": "Transações recorrentes", + "addTransactionReminder": "Adicionar lembrete de transação", + "privacyPolicyTitle": "Política de Privacidade", + "privacyCollectTitle": "Quais informações coletamos?", + "privacyChangesTitle": "Alterações na Política de Privacidade", + "contactUsTitle": "Contate-nos", + "privacyIntro": "O Sossoldi é desenvolvido como um aplicativo open source. Este serviço é fornecido gratuitamente e destina-se a ser usado como está.\nNão temos interesse em coletar nenhuma informação pessoal. Acreditamos que essas informações pertencem apenas a você. Não armazenamos nem transmitimos seus dados pessoais, nem incluímos software de publicidade ou análise que se comunique com terceiros.\n", + "privacyCollectBody": "O Sossoldi não coleta nenhuma informação pessoal e não se conecta à Internet. Qualquer informação adicionada ao aplicativo existe exclusivamente no seu dispositivo e em nenhum outro lugar.\n", + "privacyChangesBody": "Podemos atualizar nossa Política de Privacidade ocasionalmente. Portanto, recomendamos que você revise esta página periodicamente para verificar alterações.\nEsta política entra em vigor a partir de 01/01/2024.\n", + "contactUsBody": "Se você tiver dúvidas ou sugestões sobre nossa Política de Privacidade, não hesite em nos contatar em\n", + "collaboratorsTitle": "Colaboradores", + "meetTheTeam": "Conheça a equipe", + "teamDescription": "O Sossoldi é desenvolvido e mantido por uma apaixonada comunidade open source. Cada funcionalidade, correção e ideia vem de pessoas como você.", + "wantToContribute": "Quer contribuir?", + "contributeDescription": "Abra uma issue, envie um PR ou apenas diga olá no GitHub", + "appInfo": "Informações do app", + "appVersion": "Versão do app:", + "collaborators": "Colaboradores", + "collaboratorsDescription": "Conheça a equipe por trás deste app", + "privacyPolicy": "Política de Privacidade", + "privacyPolicyDescription": "Saiba mais", + "generalSettings": "Configurações gerais", + "appearance": "Aparência", + "currency": "Moeda", + "requireAuthentication": "Requerer autenticação", + "searchForATransaction": "Buscar uma transação", + "selectACurrency": "Selecionar uma moeda", + "search": "Buscar", + "searchIn": "Buscar em", + "lastTransactions": "Suas últimas transações", + "startReconciliation": "Iniciar reconciliação", + "newBalance": "Novo saldo", + "balanceDiscrepancy": "Diferença de saldo?", + "balanceAdjustmentHint": "O saldo registrado pode diferir do extrato bancário. Toque abaixo para ajustar o saldo manualmente e manter seus registros atualizados.", + "newAccount": "Nova conta", + "editAccount": "Editar conta", + "createAccount": "Criar conta", + "accountName": "Nome da conta", + "name": "Nome", + "iconAndColor": "Ícone e cor", + "chooseColor": "Escolher cor", + "chooseIcon": "Escolher ícone", + "done": "Feito", + "add": "Adicionar", + "setAsMainAccount": "Definir como conta principal", + "countsForNetWorth": "Incluir no patrimônio líquido", + "deleteAccount": "Excluir conta", + "initialBalance": "Saldo inicial", + "currentBalance": "Saldo atual", + "showLess": "Mostrar menos", + "showMore": "Mostrar mais", + "addSubcategory": "Adicionar subcategoria", + "newCategory": "Nova categoria", + "editCategory": "Editar categoria", + "createCategory": "Criar categoria", + "updateCategory": "Atualizar categoria", + "categoryName": "Nome da categoria", + "type": "Tipo", + "deleteCategory": "Excluir categoria", + "newSubcategory": "Nova subcategoria", + "editSubcategory": "Editar subcategoria", + "createSubcategory": "Criar subcategoria", + "updateSubcategory": "Atualizar subcategoria", + "subcategoryName": "Nome da subcategoria", + "deleteSubcategory": "Excluir subcategoria", + "subcategory": "Subcategoria", + "categoryFirstThenBudget": "Adicione uma categoria antes de criar um orçamento", + "inTheNextDays": "Nos próximos {next} dias", + "monthlyBudget": "Orçamento mensal", + "manage": "Gerenciar", + "swipeLeftToDelete": "Deslize para a esquerda para excluir", + "yourMonthlyBudgetWillBe": "Seu orçamento mensal será:", + "saveBudget": "Salvar orçamento", + "selectCategoriesToCreateBudget": "Selecione as categorias para criar seu orçamento", + "amount": "Valor", + "addCategoryBudget": "Adicionar orçamento à categoria", + "allCategoriesAdded": "Você já adicionou todas as categorias disponíveis.", + "delete": "Excluir", + "allRecurringPaymentsHere": "Todos os pagamentos recorrentes serão exibidos aqui", + "addRecurringPayment": "Adicionar pagamento recorrente", + "seeOlderPayments": "Ver pagamentos antigos", + "untilDate": "Até {date}", + "olderPayments": "Pagamentos antigos", + "categoryNotFound": "Categoria não encontrada", + "back": "Voltar", + "onTheDay": "- No dia {day}", + "noMonthlyPaymentHistory": "Sem histórico de pagamentos mensais", + "noRecurrentPaymentHistory": "Sem histórico de pagamentos recorrentes", + "errorLoadingPayments": "Erro ao carregar pagamentos: {error}", + "editRecurringTransaction": "Editar transação recorrente", + "detailsExplanation": "Detalhes (qualquer alteração afetará apenas transações futuras)", + "dateStart": "Data de início", + "planned": "Planejado", + "composition": "Composição", + "progress": "Progresso", + "noBudgetSet": "Nenhum orçamento definido", + "budgetHelpText": "Um orçamento mensal pode ajudá-lo a acompanhar suas despesas e manter-se dentro dos limites", + "createBudget": "Criar orçamento", + "setUpTheApp": "Configurar o app", + "setupDescription": "Em alguns passos você estará pronto para começar a\nacompanhar suas finanças pessoais (quase)\ncomo o Mr. Rip.", + "startTheSetup": "Iniciar configuração", + "budgetAmount": "Orçamento {amount}€", + "addBudget": "Adicionar orçamento", + "addBudgetForCategory": "Adicionar orçamento para a categoria {cat}", + "addCategory": "Adicionar categoria", + "confirm": "Confirmar", + "step1Of2": "Passo 1 de 2", + "setupMonthlyBudgets": "Defina seus orçamentos\nmensais", + "chooseCategoriesForBudget": "Escolha as categorias para as quais deseja definir um orçamento", + "monthlyBudgetTotal": "Total do orçamento mensal:", + "nextStep": "Próximo passo", + "continueWithoutBudget": "Continuar sem orçamento", + "step2Of2": "Passo 2 de 2", + "setLiquidityInMainAccount": "Defina a liquidez na sua conta principal", + "addMoreAccounts": "Você poderá adicionar mais contas dentro do app", + "liquidityDescription": "Ela será usada como base para adicionar receitas, despesas e calcular seu patrimônio.\nVocê poderá adicionar mais contas dentro do app.", + "mainAccount": "Conta principal", + "setAmount": "Definir valor", + "editIconAndColor": "Editar ícone e cor", + "skipStepOrStartFromZero": "Ou você pode pular este passo e começar do 0", + "startTrackingExpenses": "Começar a acompanhar suas despesas", + "startFromZero": "Começar do 0", + "importExport": "Importar/Exportar", + "importData": "Importar dados", + "importDataDescription": "Importe um arquivo CSV para atualizar o banco de dados", + "importMoneyManager": "Importar do Money Manager", + "importMoneyManagerDescription": "Importe CSV do Money Manager para atualizar o banco de dados. O arquivo deve ser salvo em formato CSV a partir de XLS.", + "exportData": "Exportar dados", + "exportDataDescription": "Salve seus dados como um arquivo CSV", + "warningOverwrite": "Atenção: Sobrescrita de dados", + "warningOverwriteContent": "A importação deste arquivo substituirá permanentemente seus dados existentes. Esta ação não pode ser desfeita. Certifique-se de ter um backup antes de prosseguir.", + "proceedImport": "Prosseguir com a importação", + "importSuccess": "Dados importados com sucesso", + "exportFailed": "Falha na exportação: {err}", + "errorExporting": "Não foi possível exportar a tabela: {tableName}", + "errorCsvNotFound": "Arquivo CSV não encontrado.", + "errorCsvEmpty": "O arquivo CSV está vazio.", + "errorCsvExpectedColumn": "Coluna faltando no CSV: {column}", + "errorCsvUnexpectedValue": "Valor inesperado encontrado: {value}", + "errorCsvImportGeneral": "Ocorreu um erro geral durante a importação do CSV. Erro: {error}", + "errorCsvTransactionImport": "Erro ao importar a transação na data: {date}", + "errorCleanDatabase": "Não foi possível limpar o banco de dados. Motivo: {error}", + "errorResetDatabase": "Não foi possível redefinir o banco de dados. Motivo: {error}", + "transactionCount": "{count} transações", + "uncategorized": "Sem categoria", + "noIncomesForSelectedMonth": "Nenhuma receita para o mês selecionado", + "noExpensesForSelectedMonth": "Nenhuma despesa para o mês selecionado", + "total": "Total", + "noTransactionsAdded": "Nenhuma transação adicionada ainda", + "addTransactionCallToAction": "Adicione uma transação para tornar esta seção mais interessante", + "graphsEmptyState": "Depois de adicionar algumas transações, gráficos incríveis aparecerão aqui... quase como mágica!", + "availableLiquidity": "Liquidez disponível", + "vsLastMonth": "VS mês anterior", + "monthlyBalance": "Saldo mensal", + "currentMonth": "Mês atual", + "lastMonth": "Mês anterior", + "yourAccounts": "Suas contas", + "yourBudgets": "Seus orçamentos", + "createBudgetToTrack": "Crie um orçamento para acompanhar suas despesas", + "close": "Fechar", + "edit": "Editar", + "errorDuplicatingTransaction": "Erro ao duplicar a transação", + "transactionCreated": "\"{transaction}\" foi criada", + "left": "Restante", + "notEnoughDataForGraph": "Lamentamos, mas não há\n-dados suficientes para criar o gráfico...", + + "generalSettingsDesc": "Editar configurações gerais", + "accountsDesc": "Adicionar ou editar suas contas", + "categoriesDesc": "Adicionar/editar categorias e subcategorias", + "budget": "Orçamento", + "budgetDesc": "Adicionar ou editar seus orçamentos", + "importExportDesc": "Importar ou exportar dados", + "notificationsDesc": "Gerenciar suas configurações de notificação", + "leaveFeedback": "Deixar feedback", + "leaveFeedbackDesc": "Preencha um pequeno formulário para relatar um bug ou deixar feedback", + "appInfoDesc": "Saiba mais sobre nós e o app" +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index cefce2b4..2dedfbb4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -8,6 +8,7 @@ import 'package:timezone/data/latest.dart' as tz; import 'package:timezone/timezone.dart' as tz; import 'package:flutter_phoenix/flutter_phoenix.dart'; +import 'l10n/app_localizations.dart'; import 'providers/settings_provider.dart'; import 'providers/theme_provider.dart'; import 'routes/routes.dart'; @@ -15,6 +16,8 @@ import 'services/database/repositories/recurring_transactions_repository.dart'; import 'services/database/sossoldi_database.dart'; import 'services/notifications/notifications_service.dart'; import 'ui/theme/app_theme.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; + void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -104,7 +107,25 @@ class Launcher extends ConsumerWidget { final appThemeState = ref.watch(appThemeStateProvider); final bool isOnboardingCompleted = ref.watch(onBoardingCompletedProvider); return MaterialApp( + supportedLocales: [ + const Locale('en'), // English + const Locale('pt'), // Portuguese + // const Locale('es'), // Spanish + const Locale('it'), // Italian + // Locale('zh'), // Chinese + // Locale('hi'), // Hindi + + ], + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + AppLocalizations.delegate + ], + locale: const Locale('pt', 'PT'), title: 'Sossoldi', + + theme: AppTheme.lightTheme, darkTheme: AppTheme.darkTheme, themeMode: appThemeState.isDarkModeEnabled diff --git a/lib/pages/accounts/account_list_page.dart b/lib/pages/accounts/account_list_page.dart index e22b2616..6cd7d166 100644 --- a/lib/pages/accounts/account_list_page.dart +++ b/lib/pages/accounts/account_list_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../constants/constants.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/widgets/default_card.dart'; import '../../ui/widgets/rounded_icon.dart'; import '../../model/bank_account.dart'; @@ -16,8 +17,12 @@ class AccountListPage extends ConsumerStatefulWidget { } class _AccountListPage extends ConsumerState { + + + @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final accountsList = ref.watch(accountsProvider); ref.listen(selectedAccountProvider, (_, _) {}); return Scaffold( @@ -26,7 +31,7 @@ class _AccountListPage extends ConsumerState { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Accounts'), + title: Text(l10n.accounts), actions: [ IconButton( onPressed: () { @@ -103,7 +108,7 @@ class _AccountListPage extends ConsumerState { }, ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ], ), diff --git a/lib/pages/accounts/account_page.dart b/lib/pages/accounts/account_page.dart index 0497e9cb..9b118d0d 100644 --- a/lib/pages/accounts/account_page.dart +++ b/lib/pages/accounts/account_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../constants/style.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/extensions.dart'; import '../../ui/widgets/line_chart.dart'; import '../../ui/widgets/transactions_list.dart'; @@ -50,6 +51,7 @@ class _AccountPage extends ConsumerState { ref: ref, ), ); + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( @@ -109,16 +111,16 @@ class _AccountPage extends ConsumerState { padding: const EdgeInsets.all(Sizes.lg), child: Column( children: [ - const Row( + Row( spacing: 8, children: [ - Icon(Icons.info_outline), - Text("Balance Discrepancy?"), + const Icon(Icons.info_outline), + Text(l10n.balanceDiscrepancy), ], ), const SizedBox(height: Sizes.sm), - const Text( - "Your recorder balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", + Text( + l10n.balanceAdjustmentHint ), const SizedBox(height: Sizes.lg), if (isRecoinciling) @@ -128,7 +130,7 @@ class _AccountPage extends ConsumerState { focusNode: _focusNode, controller: _newBalanceController, decoration: InputDecoration( - hintText: "New Balance", + hintText: l10n.newBalance, border: const OutlineInputBorder(), prefixIcon: SizedBox( width: 40, @@ -180,7 +182,7 @@ class _AccountPage extends ConsumerState { } } }, - label: const Text("Save"), + label: Text(l10n.save), icon: const Icon(Icons.check), ), ), @@ -199,8 +201,8 @@ class _AccountPage extends ConsumerState { ), onPressed: () => setState(() => isRecoinciling = false), - label: const Text( - "Cancel", + label: Text( + l10n.cancel, style: TextStyle(fontSize: 14), ), icon: const Icon(Icons.cancel_outlined), @@ -218,7 +220,7 @@ class _AccountPage extends ConsumerState { }, icon: const Icon(Icons.sync), label: Text( - "Start Reconciliation", + l10n.startReconciliation, style: Theme.of(context).textTheme.bodyMedium, ), ), @@ -233,7 +235,7 @@ class _AccountPage extends ConsumerState { top: Sizes.sm, ), child: Text( - "Your last transactions", + l10n.lastTransactions, style: Theme.of(context).textTheme.titleLarge, ), ), diff --git a/lib/pages/accounts/create_edit_account_page.dart b/lib/pages/accounts/create_edit_account_page.dart index 8691f690..7ba5c3c8 100644 --- a/lib/pages/accounts/create_edit_account_page.dart +++ b/lib/pages/accounts/create_edit_account_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import '../../providers/accounts_provider.dart'; import '../../constants/constants.dart'; import '../../constants/style.dart'; @@ -52,10 +53,11 @@ class _CreateEditAccountPage extends ConsumerState { Widget build(BuildContext context) { final selectedAccount = ref.watch(selectedAccountProvider); final currencyState = ref.watch(currencyStateProvider); + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( - title: Text("${selectedAccount == null ? "New" : "Edit"} account"), + title: Text(selectedAccount == null ? l10n.newAccount : l10n.editAccount), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), @@ -115,7 +117,7 @@ class _CreateEditAccountPage extends ConsumerState { if (context.mounted) Navigator.of(context).pop(); }, child: Text( - "${selectedAccount == null ? "CREATE" : "UPDATE"} ACCOUNT", + selectedAccount == null ? l10n.createAccount.toUpperCase() : l10n.editAccount.toUpperCase() ), ), ), @@ -143,10 +145,10 @@ class _CreateEditAccountPage extends ConsumerState { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text("NAME", style: Theme.of(context).textTheme.labelLarge), + Text(l10n.name.toUpperCase(), style: Theme.of(context).textTheme.labelLarge), TextField( controller: nameController, - decoration: const InputDecoration(hintText: "Account name"), + decoration: InputDecoration(hintText: l10n.accountName), style: Theme.of(context).textTheme.titleLarge, ), ], @@ -165,7 +167,7 @@ class _CreateEditAccountPage extends ConsumerState { Align( alignment: Alignment.centerLeft, child: Text( - "ICON AND COLOR", + l10n.iconAndColor, style: Theme.of(context).textTheme.labelLarge, ), ), @@ -193,7 +195,7 @@ class _CreateEditAccountPage extends ConsumerState { ), const SizedBox(height: Sizes.sm), Text( - "CHOOSE ICON", + l10n.chooseColor, style: Theme.of(context).textTheme.labelMedium, ), const SizedBox(height: Sizes.md), @@ -213,7 +215,7 @@ class _CreateEditAccountPage extends ConsumerState { onPressed: () => setState(() => showAccountIcons = false), child: Text( - "Done", + l10n.done, style: Theme.of(context).textTheme.bodyLarge! .copyWith( color: Theme.of( @@ -310,7 +312,7 @@ class _CreateEditAccountPage extends ConsumerState { ), const SizedBox(height: Sizes.sm), Text( - "CHOOSE COLOR", + l10n.chooseColor.toUpperCase(), style: Theme.of(context).textTheme.labelMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -340,14 +342,14 @@ class _CreateEditAccountPage extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "${selectedAccount == null ? "INITIAL" : "CURRENT"} BALANCE", + selectedAccount == null ? l10n.initialBalance.toUpperCase(): l10n.currentBalance.toUpperCase(), style: Theme.of(context).textTheme.labelLarge, ), TextField( controller: balanceController, decoration: InputDecoration( hintText: - "${selectedAccount == null ? "Initial" : "Current"} Balance", + selectedAccount == null ? l10n.initialBalance: l10n.currentBalance, suffixText: currencyState.symbol, ), keyboardType: const TextInputType.numberWithOptions( @@ -380,7 +382,7 @@ class _CreateEditAccountPage extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Set as main account", + l10n.setAsMainAccount, style: Theme.of(context).textTheme.bodyLarge, ), Switch.adaptive( @@ -398,7 +400,7 @@ class _CreateEditAccountPage extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Counts for the net worth", + l10n.countsForNetWorth, style: Theme.of(context).textTheme.bodyLarge, ), Switch.adaptive( @@ -444,7 +446,7 @@ class _CreateEditAccountPage extends ConsumerState { ), icon: const Icon(Icons.delete_outlined, color: red), label: Text( - "Delete account", + l10n.deleteAccount, style: Theme.of( context, ).textTheme.bodyLarge!.copyWith(color: red), diff --git a/lib/pages/categories/category_list_page.dart b/lib/pages/categories/category_list_page.dart index 1fd87da9..c5bef401 100644 --- a/lib/pages/categories/category_list_page.dart +++ b/lib/pages/categories/category_list_page.dart @@ -7,12 +7,15 @@ import '../../../../providers/categories_provider.dart'; import '../../../ui/device.dart'; import '../../../ui/widgets/default_card.dart'; import '../../../ui/widgets/rounded_icon.dart'; +import '../../l10n/app_localizations.dart'; class CategoryList extends ConsumerWidget { const CategoryList({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { + + var l10n = AppLocalizations.of(context)!; final categorysList = ref.watch(allParentCategoriesProvider); ref.listen(selectedCategoryProvider, (_, _) {}); return Scaffold( @@ -21,7 +24,7 @@ class CategoryList extends ConsumerWidget { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Categories'), + title: Text(l10n.categories), actions: [ IconButton( onPressed: () { @@ -98,7 +101,7 @@ class CategoryList extends ConsumerWidget { }, ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ], ), diff --git a/lib/pages/categories/create_edit_category_page.dart b/lib/pages/categories/create_edit_category_page.dart index 1040f921..0842070e 100644 --- a/lib/pages/categories/create_edit_category_page.dart +++ b/lib/pages/categories/create_edit_category_page.dart @@ -8,6 +8,7 @@ import '../../../providers/categories_provider.dart'; import '../../../providers/transactions_provider.dart'; import '../../../ui/device.dart'; import '../../../ui/extensions.dart'; +import '../../l10n/app_localizations.dart'; import 'widgets/category_icon_color_selector.dart'; import 'widgets/subcategories_list.dart'; @@ -52,10 +53,11 @@ class _CreateEditCategoryPage extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final selectedCategory = ref.watch(selectedCategoryProvider); return Scaffold( appBar: AppBar( - title: Text("${selectedCategory == null ? "New" : "Edit"} Category"), + title: Text(selectedCategory == null ? l10n.newSubcategory : l10n.editCategory), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), // Result from the .pop is used in lib\pages\planning_page\manage_budget_page.dart. @@ -119,7 +121,7 @@ class _CreateEditCategoryPage extends ConsumerState { } }, child: Text( - "${selectedCategory == null ? "CREATE" : "UPDATE"} CATEGORY", + selectedCategory == null ? l10n.createCategory.toUpperCase(): l10n.updateCategory.toUpperCase(), ), ), ), @@ -148,15 +150,15 @@ class _CreateEditCategoryPage extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "NAME", + l10n.type.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), TextField( controller: nameController, - decoration: const InputDecoration( - hintText: "Category name", + decoration: InputDecoration( + hintText: l10n.categoryName, ), style: Theme.of(context).textTheme.titleLarge, ), @@ -180,7 +182,7 @@ class _CreateEditCategoryPage extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "TYPE", + l10n.type.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -229,7 +231,7 @@ class _CreateEditCategoryPage extends ConsumerState { bottom: Sizes.sm, ), child: Text( - "SUBCATEGORY", + l10n.subcategory, style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -255,7 +257,7 @@ class _CreateEditCategoryPage extends ConsumerState { ), icon: const Icon(Icons.delete_outlined, color: red), label: Text( - "Delete category", + l10n.deleteCategory, style: Theme.of( context, ).textTheme.bodyLarge!.copyWith(color: red), diff --git a/lib/pages/categories/create_edit_subcategory_page.dart b/lib/pages/categories/create_edit_subcategory_page.dart index 40ae1f02..b6aab97b 100644 --- a/lib/pages/categories/create_edit_subcategory_page.dart +++ b/lib/pages/categories/create_edit_subcategory_page.dart @@ -6,6 +6,7 @@ import '../../../constants/style.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/categories_provider.dart'; import '../../../ui/device.dart'; +import '../../l10n/app_localizations.dart'; import 'widgets/category_icon_color_selector.dart'; class CreateEditSubcategoryPage extends ConsumerStatefulWidget { @@ -48,11 +49,12 @@ class _CreateEditSubcategoryPage @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final selectedSubcategory = ref.watch(selectedSubcategoryProvider); return Scaffold( appBar: AppBar( title: Text( - "${selectedSubcategory == null ? "New" : "Edit"} Subcategory", + selectedSubcategory == null ? l10n.newSubcategory : l10n.editSubcategory, ), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), @@ -110,7 +112,7 @@ class _CreateEditSubcategoryPage } }, child: Text( - "${selectedSubcategory == null ? "CREATE" : "UPDATE"} SUBCATEGORY", + selectedSubcategory == null ? l10n.createSubcategory.toUpperCase() : l10n.updateSubcategory.toUpperCase(), ), ), ), @@ -139,15 +141,15 @@ class _CreateEditSubcategoryPage crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "NAME", + l10n.name.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), TextField( controller: nameController, - decoration: const InputDecoration( - hintText: "Category name", + decoration: InputDecoration( + hintText: l10n.subcategoryName, ), style: Theme.of(context).textTheme.titleLarge, ), @@ -177,7 +179,7 @@ class _CreateEditSubcategoryPage ), icon: const Icon(Icons.delete_outlined, color: red), label: Text( - "Delete subcategory", + l10n.deleteSubcategory, style: Theme.of( context, ).textTheme.bodyLarge!.copyWith(color: red), diff --git a/lib/pages/categories/widgets/category_icon_color_selector.dart b/lib/pages/categories/widgets/category_icon_color_selector.dart index 59e95a74..78dd0e39 100644 --- a/lib/pages/categories/widgets/category_icon_color_selector.dart +++ b/lib/pages/categories/widgets/category_icon_color_selector.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/device.dart'; class CategoryIconColorSelector extends StatefulWidget { @@ -28,6 +29,7 @@ class _CategoryIconColorSelectorState extends State { bool showCategoryIcons = false; String selectedIconCategory = mapIconsList.keys.first; + @override void dispose() { _pageController.dispose(); @@ -36,6 +38,7 @@ class _CategoryIconColorSelectorState extends State { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return Container( margin: const EdgeInsets.symmetric( horizontal: Sizes.lg, @@ -54,7 +57,7 @@ class _CategoryIconColorSelectorState extends State { Align( alignment: Alignment.centerLeft, child: Text( - "ICON AND COLOR", + l10n.iconAndColor.toUpperCase(), style: Theme.of(context).textTheme.labelLarge?.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -82,7 +85,7 @@ class _CategoryIconColorSelectorState extends State { ), const SizedBox(height: Sizes.sm), Text( - "CHOOSE ICON", + l10n.chooseIcon.toUpperCase(), style: Theme.of(context).textTheme.labelMedium?.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -174,7 +177,7 @@ class _CategoryIconColorSelectorState extends State { ), if (widget.onColorChanged != null) Text( - "CHOOSE COLOR", + l10n.chooseColor.toUpperCase(), style: Theme.of(context).textTheme.labelMedium?.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -344,7 +347,7 @@ class _ColorGridState extends State { size: 20, ), label: Text( - showAllColors ? 'Show less' : 'Show more', + showAllColors ? AppLocalizations.of(context)!.showLess : AppLocalizations.of(context)!.showMore, style: Theme.of(context).textTheme.bodySmall, ), style: TextButton.styleFrom( diff --git a/lib/pages/categories/widgets/subcategories_list.dart b/lib/pages/categories/widgets/subcategories_list.dart index 262ab607..10972f41 100644 --- a/lib/pages/categories/widgets/subcategories_list.dart +++ b/lib/pages/categories/widgets/subcategories_list.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/categories_provider.dart'; import '../../../ui/device.dart'; @@ -48,7 +49,7 @@ class SubcategoriesList extends ConsumerWidget { color: grey1, ), Text( - "Add subcategory", + AppLocalizations.of(context)!.addSubcategory, style: Theme.of( context, ).textTheme.titleSmall!.copyWith(color: grey1), @@ -99,7 +100,7 @@ class SubcategoriesList extends ConsumerWidget { ); }, loading: () => const SizedBox.shrink(), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(AppLocalizations.of(context)!.errorOccurred(err)), ); } } diff --git a/lib/pages/dashboard/dashboard_page.dart b/lib/pages/dashboard/dashboard_page.dart index 340753f4..d2788000 100644 --- a/lib/pages/dashboard/dashboard_page.dart +++ b/lib/pages/dashboard/dashboard_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import 'widgets/account_section.dart'; import 'widgets/budgets_section.dart'; import '../../constants/style.dart'; @@ -32,6 +33,7 @@ class _HomePageState extends ConsumerState { final expense = ref.watch(expenseProvider); final currentMonthList = ref.watch(currentMonthListProvider); final lastMonthList = ref.watch(lastMonthListProvider); + var l10n = AppLocalizations.of(context)!; ref.listen( duplicatedTransactionProvider, @@ -60,7 +62,7 @@ class _HomePageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "MONTHLY BALANCE", + l10n.monthlyBalance.toUpperCase(), style: Theme.of(context).textTheme.labelMedium ?.copyWith( color: Theme.of( @@ -105,7 +107,7 @@ class _HomePageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "INCOME", + l10n.income.toUpperCase(), style: Theme.of(context).textTheme.labelMedium, ), BlurWidget( @@ -137,7 +139,7 @@ class _HomePageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "EXPENSES", + l10n.expense.toUpperCase(), style: Theme.of(context).textTheme.labelMedium, ), BlurWidget( @@ -185,7 +187,7 @@ class _HomePageState extends ConsumerState { ), const SizedBox(width: Sizes.xs), Text( - "Current month", + l10n.currentMonth, style: Theme.of(context).textTheme.labelMedium ?.copyWith( color: Theme.of(context).colorScheme.primary, @@ -202,7 +204,7 @@ class _HomePageState extends ConsumerState { ), const SizedBox(width: Sizes.xs), Text( - "Last month", + l10n.lastMonth, style: Theme.of(context).textTheme.labelMedium ?.copyWith( color: Theme.of(context).colorScheme.primary, @@ -215,7 +217,7 @@ class _HomePageState extends ConsumerState { ); }, loading: () => const SizedBox(height: 330), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), Container( decoration: BoxDecoration( @@ -238,7 +240,7 @@ class _HomePageState extends ConsumerState { Sizes.sm, ), child: Text( - "Your accounts", + l10n.yourAccounts, style: Theme.of(context).textTheme.titleLarge, ), ), @@ -253,7 +255,7 @@ class _HomePageState extends ConsumerState { Sizes.sm, ), child: Text( - "Last transactions", + l10n.lastTransactions, style: Theme.of(context).textTheme.titleLarge, ), ), @@ -264,7 +266,7 @@ class _HomePageState extends ConsumerState { transactions: transactions, ), loading: () => const SizedBox(), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), const SizedBox(height: Sizes.xxl), const BudgetsSection(), diff --git a/lib/pages/dashboard/widgets/account_section.dart b/lib/pages/dashboard/widgets/account_section.dart index a5a4c2f1..6603bb61 100644 --- a/lib/pages/dashboard/widgets/account_section.dart +++ b/lib/pages/dashboard/widgets/account_section.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../l10n/app_localizations.dart'; import 'accounts_sum.dart'; import '../../../constants/style.dart'; import '../../../model/bank_account.dart'; @@ -14,6 +15,7 @@ class AccountSection extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final accountList = ref.watch(accountsProvider); final isDarkMode = ref.watch(appThemeStateProvider).isDarkModeEnabled; return SizedBox( @@ -51,7 +53,7 @@ class AccountSection extends ConsumerWidget { padding: const EdgeInsets.all(Sizes.xs), ), label: Text( - "New Account", + l10n.newAccount, style: Theme.of(context).textTheme.bodyLarge!.copyWith( color: isDarkMode ? grey3 @@ -71,7 +73,7 @@ class AccountSection extends ConsumerWidget { }, ), loading: () => const SizedBox(), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ); } diff --git a/lib/pages/dashboard/widgets/accounts_sum.dart b/lib/pages/dashboard/widgets/accounts_sum.dart index a00eda5e..6b9b8608 100644 --- a/lib/pages/dashboard/widgets/accounts_sum.dart +++ b/lib/pages/dashboard/widgets/accounts_sum.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/bank_account.dart'; import '../../../constants/style.dart'; import '../../../providers/accounts_provider.dart'; diff --git a/lib/pages/dashboard/widgets/budgets_section.dart b/lib/pages/dashboard/widgets/budgets_section.dart index 98602ce6..af9c8277 100644 --- a/lib/pages/dashboard/widgets/budgets_section.dart +++ b/lib/pages/dashboard/widgets/budgets_section.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/widgets/budget_circular_indicator.dart'; import '../../../providers/budgets_provider.dart'; import '../../../ui/device.dart'; @@ -12,6 +13,7 @@ class BudgetsSection extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final budgetsAsync = ref.watch(monthlyBudgetsStatsProvider); + var l10n = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.start, spacing: Sizes.lg, @@ -21,7 +23,7 @@ class BudgetsSection extends ConsumerWidget { child: Padding( padding: const EdgeInsets.only(left: Sizes.lg), child: Text( - "Your budgets", + l10n.yourBudgets, style: Theme.of(context).textTheme.titleLarge, ), ), @@ -37,14 +39,14 @@ class BudgetsSection extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "No budget set", + l10n.noBudgetSet, style: Theme.of(context).textTheme.titleMedium?.copyWith( color: Colors.grey[600], ), ), const SizedBox(height: Sizes.sm), Text( - "Create a budget to track your spending", + l10n.createBudgetToTrack, style: Theme.of( context, ).textTheme.bodySmall?.copyWith(color: Colors.grey[500]), @@ -79,7 +81,7 @@ class BudgetsSection extends ConsumerWidget { ); } }, - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), loading: () => const Center(child: CircularProgressIndicator()), ), ], diff --git a/lib/pages/graphs/graphs_page.dart b/lib/pages/graphs/graphs_page.dart index da5c9c73..e3b56960 100644 --- a/lib/pages/graphs/graphs_page.dart +++ b/lib/pages/graphs/graphs_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/extensions.dart'; import '../../ui/widgets/line_chart.dart'; import '../../model/transaction.dart'; @@ -25,7 +26,7 @@ class _GraphsPageState extends ConsumerState { currentYearMontlyTransactionsProvider, ); final currencyState = ref.watch(currencyStateProvider); - + var l10n = AppLocalizations.of(context)!; return ListView( children: [ const SizedBox(height: Sizes.lg), @@ -59,7 +60,7 @@ class _GraphsPageState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( - "Available liquidity", + l10n.availableLiquidity, style: Theme.of(context).textTheme.titleLarge ?.copyWith( color: Theme.of( @@ -131,7 +132,7 @@ class _GraphsPageState extends ConsumerState { ), ), Text( - " VS last month", + l10n.vsLastMonth, style: Theme.of(context).textTheme.labelLarge ?.copyWith(fontWeight: FontWeight.w300), ), @@ -149,7 +150,7 @@ class _GraphsPageState extends ConsumerState { ); }, loading: () => const SizedBox(), - error: (error, stack) => Text('Error: $error'), + error: (error, stack) => Text(l10n.errorOccurred(error)), ), const SizedBox(height: Sizes.xl), const AccountsCard(), diff --git a/lib/pages/graphs/widgets/accounts/accounts_card.dart b/lib/pages/graphs/widgets/accounts/accounts_card.dart index 460234ee..67e29545 100644 --- a/lib/pages/graphs/widgets/accounts/accounts_card.dart +++ b/lib/pages/graphs/widgets/accounts/accounts_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/device.dart'; import '../../../../ui/extensions.dart'; import '../linear_progress_bar.dart'; @@ -17,10 +18,10 @@ class AccountsCard extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final accountList = ref.watch(accountsProvider); final currencyState = ref.watch(currencyStateProvider); - + var l10n = AppLocalizations.of(context)!; return Column( children: [ - const CardLabel(label: "Accounts"), + CardLabel(label: l10n.accounts), const SizedBox(height: Sizes.sm), DefaultContainer( child: accountList.when( @@ -76,7 +77,7 @@ class AccountsCard extends ConsumerWidget { }, ), loading: () => const SizedBox.shrink(), - error: (e, s) => Text('Error: $e'), + error: (e, s) => Text(l10n.errorOccurred(e)), ), ), ], diff --git a/lib/pages/graphs/widgets/categories/categories_card.dart b/lib/pages/graphs/widgets/categories/categories_card.dart index 178ab0f2..d63646f8 100644 --- a/lib/pages/graphs/widgets/categories/categories_card.dart +++ b/lib/pages/graphs/widgets/categories/categories_card.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/category_type_button.dart'; import '../../../../ui/widgets/default_container.dart'; import '../../../../model/category_transaction.dart'; @@ -27,11 +28,11 @@ class CategoriesCardState extends ConsumerState { final categoryMap = ref.watch(categoryMapProvider); final categoryTotalAmount = ref.watch(categoryTotalAmountProvider).value ?? 0; - + var l10n = AppLocalizations.of(context)!; return Column( spacing: Sizes.sm, children: [ - const CardLabel(label: "Categories"), + CardLabel(label: l10n.categories), DefaultContainer( child: Column( spacing: Sizes.xl, @@ -52,7 +53,7 @@ class CategoriesCardState extends ConsumerState { loading: () => LoadingContentWidget( previousCategoriesCount: _categoriesCount, ), - error: (e, s) => Text("Error: $e"), + error: (e, s) => Text(l10n.errorOccurred(e)), ), ], ), @@ -175,11 +176,12 @@ class NoTransactionsContent extends StatelessWidget { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return SizedBox( height: 200, child: Center( child: Text( - "After you add some transactions, some outstanding graphs will appear here... almost by magic!", + l10n.graphsEmptyState, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), diff --git a/lib/pages/graphs/widgets/categories/categories_graph_pie_chart.dart b/lib/pages/graphs/widgets/categories/categories_graph_pie_chart.dart index 3c6e9403..fdbd9d83 100644 --- a/lib/pages/graphs/widgets/categories/categories_graph_pie_chart.dart +++ b/lib/pages/graphs/widgets/categories/categories_graph_pie_chart.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../constants/constants.dart'; import '../../../../constants/style.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../model/category_transaction.dart'; import '../../../../providers/categories_provider.dart'; @@ -123,7 +124,7 @@ class PieChartCategoryInfo extends ConsumerWidget { if (selectedCategory != null) Text(selectedCategory.name) else - const Text("Total"), + Text(AppLocalizations.of(context)!.total), ], ); } diff --git a/lib/pages/onboarding/onboarding_page.dart b/lib/pages/onboarding/onboarding_page.dart index 1bb2d8d8..12fbe86c 100644 --- a/lib/pages/onboarding/onboarding_page.dart +++ b/lib/pages/onboarding/onboarding_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/assets.dart'; import '../../ui/device.dart'; import 'widgets/budget_setup.dart'; @@ -14,6 +15,7 @@ class Onboarding extends StatefulWidget { class _OnboardingState extends State { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: blue7, body: SafeArea( @@ -26,7 +28,7 @@ class _OnboardingState extends State { children: [ SizedBox(height: MediaQuery.sizeOf(context).height / 9), Text( - 'Set up the app', + l10n.setUpTheApp, style: Theme.of( context, ).textTheme.headlineLarge?.copyWith(color: blue1), @@ -38,7 +40,7 @@ class _OnboardingState extends State { ), const SizedBox(height: 74), Text( - 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip', + l10n.setupDescription, textAlign: TextAlign.center, style: Theme.of( context, @@ -60,7 +62,7 @@ class _OnboardingState extends State { ), ); }, - child: const Center(child: Text('START THE SET UP')), + child: Center(child: Text(l10n.startTheSetup)), ), ), ], diff --git a/lib/pages/onboarding/widgets/account_setup.dart b/lib/pages/onboarding/widgets/account_setup.dart index d91db7fc..709f06d4 100644 --- a/lib/pages/onboarding/widgets/account_setup.dart +++ b/lib/pages/onboarding/widgets/account_setup.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; +import '../../../l10n/app_localizations_en.dart'; import '../../../providers/accounts_provider.dart'; import '../../../ui/formatters/decimal_text_input_formatter.dart'; import '../../../ui/device.dart'; @@ -47,6 +49,7 @@ class _AccountSetupState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: blue7, resizeToAvoidBottomInset: false, @@ -56,12 +59,12 @@ class _AccountSetupState extends ConsumerState { child: Column( children: [ Text( - "STEP 2 OF 2", + l10n.setupMonthlyBudgets, style: Theme.of(context).textTheme.labelSmall, ), const SizedBox(height: Sizes.xl), Text( - "Set the liquidity in your main account", + l10n.setLiquidityInMainAccount, textAlign: TextAlign.center, style: Theme.of( context, @@ -69,7 +72,7 @@ class _AccountSetupState extends ConsumerState { ), const SizedBox(height: Sizes.xl), Text( - "It will be used as a baseline to which you can add income, expenses and calculate your wealth.", + l10n.liquidityDescription, textAlign: TextAlign.center, maxLines: 3, style: Theme.of( @@ -78,7 +81,7 @@ class _AccountSetupState extends ConsumerState { ), const SizedBox(height: Sizes.sm), Text( - "You'll be able to add more accounts within the app.", + l10n.addMoreAccounts, textAlign: TextAlign.center, maxLines: 3, style: Theme.of( @@ -120,7 +123,7 @@ class _AccountSetupState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "ACCOUNT NAME ", + l10n.accountName.toUpperCase(), style: Theme.of( context, ).textTheme.labelSmall?.copyWith(color: grey1), @@ -133,7 +136,7 @@ class _AccountSetupState extends ConsumerState { controller: accountNameController, autofocus: true, decoration: InputDecoration( - hintText: "Main Account", + hintText: l10n.mainAccount, errorStyle: Theme.of(context).textTheme.bodyLarge ?.copyWith(fontSize: 10, color: red), hintStyle: Theme.of(context).textTheme.bodySmall, @@ -155,7 +158,7 @@ class _AccountSetupState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "SET AMOUNT ", + l10n.setAmount.toUpperCase(), style: Theme.of( context, ).textTheme.labelSmall?.copyWith(color: grey1), @@ -198,7 +201,7 @@ class _AccountSetupState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - "EDIT ICON AND COLOR ", + l10n.editIconAndColor.toUpperCase(), style: Theme.of( context, ).textTheme.labelSmall?.copyWith(color: grey1), @@ -323,7 +326,7 @@ class _AccountSetupState extends ConsumerState { children: [ const SizedBox(height: Sizes.lg), Text( - 'Or you can skip this step and start from 0', + l10n.skipStepOrStartFromZero, style: Theme.of( context, ).textTheme.bodySmall?.copyWith(color: blue1), @@ -349,7 +352,7 @@ class _AccountSetupState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'START FROM 0 ', + l10n.startFromZero.toUpperCase(), style: Theme.of( context, ).textTheme.bodyMedium?.copyWith(color: blue1), @@ -397,8 +400,8 @@ class _AccountSetupState extends ConsumerState { style: ElevatedButton.styleFrom( backgroundColor: _validAmount ? blue5 : grey2, ), - child: const Center( - child: Text('START TRACKING YOUR EXPENSES'), + child: Center( + child: Text(l10n.startTrackingExpenses), ), ), ), diff --git a/lib/pages/onboarding/widgets/add_budget_dialog.dart b/lib/pages/onboarding/widgets/add_budget_dialog.dart index 9b103353..6504efae 100644 --- a/lib/pages/onboarding/widgets/add_budget_dialog.dart +++ b/lib/pages/onboarding/widgets/add_budget_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/budget.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/budgets_provider.dart'; @@ -52,9 +53,10 @@ class _AddBudgetState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return AlertDialog( title: Text( - 'Add budget for ${widget.category.name}', + l10n.addBudgetForCategory(widget.category.name), style: Theme.of(context).textTheme.bodyMedium, textAlign: TextAlign.center, ), @@ -65,7 +67,7 @@ class _AddBudgetState extends ConsumerState { actions: [ TextButton( onPressed: () => Navigator.pop(context), - child: Text('CANCEL', style: Theme.of(context).textTheme.bodyMedium), + child: Text(l10n.cancel, style: Theme.of(context).textTheme.bodyMedium), ), ElevatedButton( onPressed: () async { @@ -93,7 +95,7 @@ class _AddBudgetState extends ConsumerState { ), ), child: Text( - 'CONFIRM', + l10n.confirm, style: Theme.of(context).textTheme.bodyMedium?.apply(color: white), ), ), diff --git a/lib/pages/onboarding/widgets/add_category_button.dart b/lib/pages/onboarding/widgets/add_category_button.dart index e7da8e7f..e6652b51 100644 --- a/lib/pages/onboarding/widgets/add_category_button.dart +++ b/lib/pages/onboarding/widgets/add_category_button.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/device.dart'; class AddCategoryButton extends StatelessWidget { @@ -28,7 +29,7 @@ class AddCategoryButton extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Text( - "Add category", + AppLocalizations.of(context)!.addCategory, style: Theme.of( context, ).textTheme.bodyMedium?.copyWith(color: grey1), diff --git a/lib/pages/onboarding/widgets/budget_setup.dart b/lib/pages/onboarding/widgets/budget_setup.dart index 89f84100..4abfc532 100644 --- a/lib/pages/onboarding/widgets/budget_setup.dart +++ b/lib/pages/onboarding/widgets/budget_setup.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/budget.dart'; import '../../../providers/budgets_provider.dart'; import '../../../providers/categories_provider.dart'; @@ -29,6 +30,7 @@ class _BudgetSetupState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; budgetsList = ref.watch(budgetsProvider).value; totalBudget = budgetsList?.fold( @@ -45,12 +47,12 @@ class _BudgetSetupState extends ConsumerState { child: Column( children: [ Text( - "STEP 1 OF 2", + l10n.step1Of2, style: Theme.of(context).textTheme.labelSmall, ), const SizedBox(height: Sizes.xl), Text( - "Set up your monthly\nbudgets", + l10n.setupMonthlyBudgets, textAlign: TextAlign.center, style: Theme.of( context, @@ -58,7 +60,7 @@ class _BudgetSetupState extends ConsumerState { ), const SizedBox(height: Sizes.xxl), Text( - "Choose which categories you want to set a budget for", + l10n.chooseCategoriesForBudget, textAlign: TextAlign.center, style: Theme.of( context, @@ -123,7 +125,7 @@ class _BudgetSetupState extends ConsumerState { } }, ), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), loading: () => const Center(child: CircularProgressIndicator()), ), @@ -137,7 +139,7 @@ class _BudgetSetupState extends ConsumerState { children: [ const SizedBox(height: Sizes.sm), Text( - "Monthly budget total:", + l10n.monthlyBudgetTotal, style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: Sizes.sm), @@ -184,7 +186,7 @@ class _BudgetSetupState extends ConsumerState { ), ), child: Text( - 'NEXT STEP', + l10n.nextStep, style: Theme.of(context).textTheme.bodyMedium ?.copyWith(color: Colors.white), ), @@ -213,7 +215,7 @@ class _BudgetSetupState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.center, children: [ Text( - 'CONTINUE WITHOUT BUDGET ', + l10n.continueWithoutBudget.toUpperCase(), style: Theme.of( context, ).textTheme.bodyMedium?.copyWith(color: blue1), diff --git a/lib/pages/onboarding/widgets/category_button.dart b/lib/pages/onboarding/widgets/category_button.dart index 4b241909..eba479b6 100644 --- a/lib/pages/onboarding/widgets/category_button.dart +++ b/lib/pages/onboarding/widgets/category_button.dart @@ -3,6 +3,7 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/budget.dart'; import '../../../ui/device.dart'; @@ -20,6 +21,7 @@ class CategoryButton extends StatelessWidget { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; if (budget != null && budget!.active && budget!.amountLimit > 0) { return Container( decoration: BoxDecoration( @@ -51,7 +53,7 @@ class CategoryButton extends StatelessWidget { ).textTheme.bodyLarge?.copyWith(color: white), ), Text( - "BUDGET: ${budget?.amountLimit}€", + l10n.budgetAmount(budget!.amountLimit).toUpperCase(), style: Theme.of( context, ).textTheme.bodyLarge?.copyWith(fontSize: 10, color: white), @@ -96,7 +98,7 @@ class CategoryButton extends StatelessWidget { overflow: TextOverflow.ellipsis, ), Text( - "ADD BUDGET", + l10n.addBudget.toUpperCase(), style: Theme.of(context).textTheme.labelMedium, ), ], diff --git a/lib/pages/planning/manage_budget_page.dart b/lib/pages/planning/manage_budget_page.dart index 1c472ff2..a74dcb41 100644 --- a/lib/pages/planning/manage_budget_page.dart +++ b/lib/pages/planning/manage_budget_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import '../../model/budget.dart'; import '../../model/category_transaction.dart'; import '../../providers/currency_provider.dart'; @@ -61,8 +62,8 @@ class _ManageBudgetPageState extends ConsumerState { void handleEmptyCategories() { showSnackBar( context, - message: "Add a category first to set a budget", - actionLabel: "ADD", + message: AppLocalizations.of(context)!.categoryFirstThenBudget, + actionLabel: AppLocalizations.of(context)!.add.toUpperCase(), onAction: () async { final categoryAdded = await Navigator.pushNamed( @@ -85,6 +86,7 @@ class _ManageBudgetPageState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final availableCategories = categories .where((c) => !usedCategoryIds.contains(c.id)) .toList(); @@ -97,7 +99,7 @@ class _ManageBudgetPageState extends ConsumerState { Column( children: [ Text( - "Swipe left to delete", + l10n.swipeLeftToDelete, style: Theme.of(context).textTheme.bodySmall, ), const SizedBox(height: Sizes.md), @@ -107,7 +109,7 @@ class _ManageBudgetPageState extends ConsumerState { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Your monthly budget will be: ", + l10n.yourMonthlyBudgetWillBe, style: Theme.of(context).textTheme.titleMedium, ), Text.rich( @@ -147,7 +149,7 @@ class _ManageBudgetPageState extends ConsumerState { Navigator.of(context).pop(); } }, - child: const Text("SAVE BUDGET"), + child: Text(l10n.saveBudget), ), ), ], @@ -160,20 +162,20 @@ class _ManageBudgetPageState extends ConsumerState { Padding( padding: const EdgeInsets.all(Sizes.lg), child: Text( - "Select the categories to create your budget", + l10n.selectCategoriesToCreateBudget, style: Theme.of(context).textTheme.titleLarge, ), ), - const Padding( - padding: EdgeInsets.symmetric( + Padding( + padding: const EdgeInsets.symmetric( horizontal: Sizes.lg, vertical: Sizes.sm, ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('CATEGORY', textAlign: TextAlign.left), - Text('AMOUNT', textAlign: TextAlign.right), + Text(l10n.category.toUpperCase(), textAlign: TextAlign.left), + Text(l10n.amount.toUpperCase(), textAlign: TextAlign.right), ], ), ), @@ -201,16 +203,16 @@ class _ManageBudgetPageState extends ConsumerState { name: availableCategories[0].name, ), ), - label: const Text("Add category budget"), + label: Text(l10n.addCategoryBudget), ), ); } else { - return const Padding( - padding: EdgeInsets.only(top: 8.0), + return Padding( + padding: const EdgeInsets.only(top: 8.0), child: Center( child: Text( - "You have already added all available categories.", - style: TextStyle(color: Colors.grey), + l10n.allCategoriesAdded, + style: const TextStyle(color: Colors.grey), ), ), ); @@ -230,10 +232,10 @@ class _ManageBudgetPageState extends ConsumerState { padding: const EdgeInsets.only(right: Sizes.lg), alignment: Alignment.centerRight, color: Colors.red, - child: const Text( - 'Delete', + child: Text( + l10n.delete, textAlign: TextAlign.right, - style: TextStyle(color: Colors.white), + style: const TextStyle(color: Colors.white), ), ), onDismissed: (_) => _deleteBudget(index), diff --git a/lib/pages/planning/planning_page.dart b/lib/pages/planning/planning_page.dart index 40ad249c..7dc9c8ee 100644 --- a/lib/pages/planning/planning_page.dart +++ b/lib/pages/planning/planning_page.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/device.dart'; import 'manage_budget_page.dart'; import 'widget/budget_card.dart'; @@ -9,6 +10,7 @@ class PlanningPage extends StatelessWidget { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return ListView( padding: const EdgeInsetsDirectional.all(Sizes.md), children: [ @@ -16,7 +18,7 @@ class PlanningPage extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "Monthly budget", + l10n.monthlyBudget, style: Theme.of(context).textTheme.titleLarge, ), GestureDetector( @@ -43,7 +45,7 @@ class PlanningPage extends StatelessWidget { child: Row( spacing: Sizes.xs, children: [ - Text("MANAGE", style: Theme.of(context).textTheme.labelLarge), + Text(l10n.manage.toUpperCase(), style: Theme.of(context).textTheme.labelLarge), const Icon(Icons.edit, size: 13), ], ), @@ -54,7 +56,7 @@ class PlanningPage extends StatelessWidget { const BudgetCard(), const SizedBox(height: Sizes.xl), Text( - "Recurring payments", + l10n.recurringPayments, style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: Sizes.sm), diff --git a/lib/pages/planning/widget/budget_card.dart b/lib/pages/planning/widget/budget_card.dart index f7f3ddf0..8148b4f8 100644 --- a/lib/pages/planning/widget/budget_card.dart +++ b/lib/pages/planning/widget/budget_card.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/categories_provider.dart'; import '../../../ui/extensions.dart'; @@ -24,6 +25,7 @@ class BudgetCard extends ConsumerWidget { final transactionsAsync = ref.watch(monthlyTransactionsProvider); final categories = ref.watch(allParentCategoriesProvider).value ?? []; final currencyState = ref.watch(currencyStateProvider); + var l10n = AppLocalizations.of(context)!; return DefaultContainer( margin: EdgeInsets.zero, @@ -36,7 +38,7 @@ class BudgetCard extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "Composition", + l10n.composition, style: Theme.of(context).textTheme.titleLarge, ), BudgetPieChart( @@ -44,7 +46,7 @@ class BudgetCard extends ConsumerWidget { categories: categories, ), Text( - "Progress", + l10n.progress, style: Theme.of(context).textTheme.titleLarge, ), const SizedBox(height: Sizes.sm), @@ -115,19 +117,19 @@ class BudgetCard extends ConsumerWidget { loading: () => const Center(child: CircularProgressIndicator()), error: (err, stack) { - return Text('Error: $err'); + return Text(l10n.errorOccurred(err)); }, ) : Column( children: [ Text( - "There are no budget set", + l10n.noBudgetSet, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), Image.asset(SossoldiAssets.wallet, width: 240, height: 240), Text( - "A monthly budget can help you keep track of your expenses and stay within the limits", + l10n.budgetHelpText, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -147,7 +149,7 @@ class BudgetCard extends ConsumerWidget { size: Sizes.xl, ), label: Text( - "Create budget", + l10n.createBudget, style: Theme.of(context).textTheme.titleLarge!.apply( color: Theme.of( context, @@ -192,7 +194,7 @@ class BudgetCard extends ConsumerWidget { ); }, loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ); } diff --git a/lib/pages/planning/widget/budget_category_selector.dart b/lib/pages/planning/widget/budget_category_selector.dart index f978005f..2940af6e 100644 --- a/lib/pages/planning/widget/budget_category_selector.dart +++ b/lib/pages/planning/widget/budget_category_selector.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/budget.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/currency_provider.dart'; @@ -59,6 +60,7 @@ class _BudgetCategorySelector extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final currencyState = ref.watch(currencyStateProvider); return Container( padding: const EdgeInsets.all(Sizes.lg), diff --git a/lib/pages/planning/widget/budget_pie_chart.dart b/lib/pages/planning/widget/budget_pie_chart.dart index 80c0b155..6ea320ec 100644 --- a/lib/pages/planning/widget/budget_pie_chart.dart +++ b/lib/pages/planning/widget/budget_pie_chart.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/budget.dart'; import '../../../model/category_transaction.dart'; import '../../../providers/currency_provider.dart'; @@ -39,6 +40,7 @@ class BudgetPieChart extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final currencyState = ref.watch(currencyStateProvider); double totalBudget = 0; for (Budget budget in budgets) { @@ -64,8 +66,8 @@ class BudgetPieChart extends ConsumerWidget { "${totalBudget.toCurrency()}${currencyState.symbol}", style: const TextStyle(fontSize: 25), ), - const Text( - "PLANNED", + Text( + l10n.planned.toUpperCase(), style: TextStyle(fontWeight: FontWeight.normal), ), ], diff --git a/lib/pages/planning/widget/edit_recurring_transaction.dart b/lib/pages/planning/widget/edit_recurring_transaction.dart index 05e67443..81db55b3 100644 --- a/lib/pages/planning/widget/edit_recurring_transaction.dart +++ b/lib/pages/planning/widget/edit_recurring_transaction.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/categories_provider.dart'; import '../../../providers/recurring_transactions_provider.dart'; import '../../../providers/transactions_provider.dart'; @@ -50,6 +51,7 @@ class _EditRecurringTransactionState @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; final selectedRecurringTransaction = ref.watch( selectedRecurringTransactionUpdateProvider, ); diff --git a/lib/pages/planning/widget/older_recurring_payments.dart b/lib/pages/planning/widget/older_recurring_payments.dart index 5ddedd2e..7fe0e715 100644 --- a/lib/pages/planning/widget/older_recurring_payments.dart +++ b/lib/pages/planning/widget/older_recurring_payments.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/category_transaction.dart'; import '../../../model/currency.dart'; import '../../../model/recurring_transaction.dart'; @@ -22,6 +23,7 @@ class OlderRecurringPayments extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final String nextDueDay = getNextDueDay(); final currencyState = ref.watch(currencyStateProvider); final categories = ref.watch(categoriesProvider).value; @@ -35,14 +37,14 @@ class OlderRecurringPayments extends ConsumerWidget { // Handle null category case if (category == null) { return Scaffold( - appBar: AppBar(title: const Text("Older payments"), centerTitle: true), - body: const Center(child: Text("Category not found")), + appBar: AppBar(title: Text(l10n.olderPayments), centerTitle: true), + body: Center(child: Text(l10n.categoryNotFound)), ); } return Scaffold( appBar: AppBar( - title: const Text("Older payments"), + title: Text(l10n.olderPayments), centerTitle: true, leadingWidth: 80.0, leading: InkWell( @@ -54,7 +56,7 @@ class OlderRecurringPayments extends ConsumerWidget { const SizedBox(width: Sizes.sm), const Icon(Icons.arrow_back_ios), Text( - "Back", + l10n.back, style: Theme.of( context, ).textTheme.titleMedium!.copyWith(color: darkBlue5), @@ -76,8 +78,8 @@ class OlderRecurringPayments extends ConsumerWidget { ), const SizedBox(height: Sizes.sm), Text( - "${transaction.recurrency.label}" - " - On the $nextDueDay day", + transaction.recurrency.label + + l10n.onTheDay(nextDueDay), style: Theme.of(context).textTheme.bodyLarge, ), const SizedBox(height: Sizes.xl), @@ -186,7 +188,7 @@ class OlderRecurringPayments extends ConsumerWidget { ).colorScheme.surface, child: Center( child: Text( - "No Montly payment history", + l10n.noMonthlyPaymentHistory, style: Theme.of( context, ).textTheme.bodySmall, @@ -200,7 +202,7 @@ class OlderRecurringPayments extends ConsumerWidget { ) : Center( child: Text( - "No recurrent payment history", + l10n.noRecurrentPaymentHistory, style: Theme.of(context).textTheme.titleMedium, ), ); @@ -208,7 +210,7 @@ class OlderRecurringPayments extends ConsumerWidget { loading: () => const Center(child: CircularProgressIndicator()), error: (error, stackTrace) => Center( child: Text( - "Error loading payments: $error", + l10n.errorLoadingPayments(error), style: Theme.of(context).textTheme.titleMedium, ), ), diff --git a/lib/pages/planning/widget/recurring_payment_card.dart b/lib/pages/planning/widget/recurring_payment_card.dart index ff185864..6dcb31a2 100644 --- a/lib/pages/planning/widget/recurring_payment_card.dart +++ b/lib/pages/planning/widget/recurring_payment_card.dart @@ -2,6 +2,7 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/extensions.dart'; import '../../../ui/widgets/rounded_icon.dart'; import '../../../model/recurring_transaction.dart'; @@ -32,6 +33,7 @@ class RecurringPaymentCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final categories = ref.watch(categoriesProvider).value; final accounts = ref.watch(accountsProvider).value; final isDarkMode = ref.watch(appThemeStateProvider).isDarkModeEnabled; @@ -100,7 +102,7 @@ class RecurringPaymentCard extends ConsumerWidget { spacing: Sizes.sm, children: [ Text( - "IN ${getNextText()} DAYS".toUpperCase(), + l10n.inTheNextDays(getNextText()).toUpperCase(), style: Theme.of(context).textTheme.labelLarge, ), Builder( @@ -183,8 +185,8 @@ class RecurringPaymentCard extends ConsumerWidget { ), ), icon: const Icon(Icons.checklist_rtl_outlined), - label: const Text( - "See older payments", + label: Text( + l10n.seeOlderPayments, style: TextStyle(fontSize: 14), ), ), diff --git a/lib/pages/planning/widget/recurring_payments_list.dart b/lib/pages/planning/widget/recurring_payments_list.dart index e60707b1..bf0987fa 100644 --- a/lib/pages/planning/widget/recurring_payments_list.dart +++ b/lib/pages/planning/widget/recurring_payments_list.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/recurring_transactions_provider.dart'; import 'recurring_payment_card.dart'; import '../../../model/recurring_transaction.dart'; @@ -15,6 +16,7 @@ class RecurringPaymentSection extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; var recurringTransactionsAsync = ref.watch(recurringTransactionsProvider); void addRecurringPayment() { @@ -43,7 +45,7 @@ class RecurringPaymentSection extends ConsumerWidget { spacing: Sizes.lg, children: [ Text( - "All recurring payments will be displayed here", + l10n.allRecurringPaymentsHere, style: Theme.of(context).textTheme.bodySmall, ), Container( @@ -61,7 +63,7 @@ class RecurringPaymentSection extends ConsumerWidget { size: Sizes.xl, ), label: Text( - "Add recurring payment", + l10n.addRecurringPayment, style: Theme.of(context).textTheme.titleLarge!.apply( color: Theme.of( context, @@ -112,7 +114,7 @@ class RecurringPaymentSection extends ConsumerWidget { TextButton.icon( icon: const Icon(Icons.add_circle, size: 32), onPressed: addRecurringPayment, - label: const Text("Add recurring payment"), + label: Text(l10n.addRecurringPayment), ), ], ); @@ -121,7 +123,7 @@ class RecurringPaymentSection extends ConsumerWidget { return const CircularProgressIndicator(); }, error: (error, _) { - return Text('Error: $error'); + return Text(l10n.errorOccurred(error)); }, ), ); diff --git a/lib/pages/search/search_page.dart b/lib/pages/search/search_page.dart index 44905ad7..e48c0510 100644 --- a/lib/pages/search/search_page.dart +++ b/lib/pages/search/search_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../providers/accounts_provider.dart'; import '../../../providers/transactions_provider.dart'; +import '../../l10n/app_localizations.dart'; import '../../services/database/repositories/transactions_repository.dart'; import '../../ui/extensions.dart'; import '../../ui/widgets/transactions_list.dart'; @@ -34,10 +35,10 @@ class _SearchPage extends ConsumerState { final accountList = ref.watch(accountsProvider); final filterAccountList = ref.watch(filterAccountProvider); final searchTransactions = ref.watch(searchTransactionsProvider); - + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( - title: const Text("Search"), + title: Text(l10n.search), leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), @@ -54,11 +55,11 @@ class _SearchPage extends ConsumerState { borderRadius: BorderRadius.circular(Sizes.borderRadius), ), child: InputDecorator( - decoration: const InputDecoration( - prefixIcon: Icon(Icons.search), + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search), border: InputBorder.none, - contentPadding: EdgeInsets.symmetric(horizontal: Sizes.sm), - hintText: "Search", + contentPadding: const EdgeInsets.symmetric(horizontal: Sizes.sm), + hintText: l10n.search, ), child: Autocomplete( optionsBuilder: (TextEditingValue textEditingValue) { @@ -79,18 +80,31 @@ class _SearchPage extends ConsumerState { ), ), const SizedBox(height: Sizes.md), - Text("SEARCH FOR", style: Theme.of(context).textTheme.bodySmall), + Text(l10n.searchForATransaction, style: Theme.of(context).textTheme.bodySmall), SizedBox( height: 60, child: ListView( scrollDirection: Axis.horizontal, children: TransactionType.values.map((type) { + String message = ''; + switch(type) + { + case TransactionType.expense: + message = l10n.expense; + break; + case TransactionType.income: + message = l10n.income; + break; + case TransactionType.transfer: + message = l10n.transfer; + break; + } return Padding( padding: const EdgeInsets.symmetric(horizontal: Sizes.sm), child: FilterChip( showCheckmark: false, label: Text( - type.name.capitalize(), + message, style: TextStyle( color: filterType[type.code]! ? Colors.white @@ -112,7 +126,7 @@ class _SearchPage extends ConsumerState { ), ), const SizedBox(height: Sizes.md), - Text("SEARCH IN", style: Theme.of(context).textTheme.bodySmall), + Text(l10n.searchIn, style: Theme.of(context).textTheme.bodySmall), SizedBox( height: 60, child: accountList.when( @@ -159,7 +173,7 @@ class _SearchPage extends ConsumerState { ); }, loading: () => const SizedBox(), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ), Expanded( @@ -173,13 +187,13 @@ class _SearchPage extends ConsumerState { ), ); } else { - return const Center( - child: Text("Search for a transaction"), + return Center( + child: Text(l10n.searchForATransaction), ); } }, loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ), ], diff --git a/lib/pages/settings/backup/backup_page.dart b/lib/pages/settings/backup/backup_page.dart index fef1c764..8d4ee26a 100644 --- a/lib/pages/settings/backup/backup_page.dart +++ b/lib/pages/settings/backup/backup_page.dart @@ -3,6 +3,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_phoenix/flutter_phoenix.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import '../../../constants/exceptions.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../services/database/sossoldi_database.dart'; import '../../../ui/device.dart'; import '../../../services/csv/csv_file_picker.dart'; @@ -10,6 +13,7 @@ import '../../../ui/snack_bars/snack_bar.dart'; import '../../../ui/widgets/default_card.dart'; + class BackupPage extends ConsumerStatefulWidget { const BackupPage({super.key}); @@ -49,6 +53,32 @@ enum CsvSource{ sossoldi, moneyManager } +void _showLocalizedError(BuildContext context, Object error) { + final l10n = AppLocalizations.of(context)!; + String message = ''; + + if (error is CsvExportingErrorException) { + message = l10n.errorExporting(error.tableName); + } else if (error is CsvImportGeneralErrorException) { + message = l10n.errorCsvImportGeneral(error.text); + } else if (error is CsvNotFoundException) { + message = l10n.errorCsvNotFound; + } else if (error is CsvEmptyException) { + message = l10n.errorCsvEmpty; + } else if (error is CsvExpectedColumnException) { + message = l10n.errorCsvExpectedColumn(error.column); + } else if (error is CsvUnexpectedValueException) { + message = l10n.errorCsvUnexpectedValue(error.value); + } else if (error is CsvTransactionImportErrorException) { + message = l10n.errorCsvTransactionImport(error.date); + } else if (error is CleanDatabaseException) { + message = l10n.errorCleanDatabase(error.text); + } else if (error is ResetDatabaseException) { + message = l10n.errorCleanDatabase(error.text); + } + + showSnackBar(context, message: message); +} class _BackupPageState extends ConsumerState { @@ -61,7 +91,7 @@ class _BackupPageState extends ConsumerState { if (!mounted) return; - CSVFilePicker.showLoading(context, 'Importing data...'); + CSVFilePicker.showLoading(context, AppLocalizations.of(context)!.importData); switch(source) { @@ -76,7 +106,7 @@ class _BackupPageState extends ConsumerState { if (results.values.every((success) => success)) { await CSVFilePicker.showSuccess( context, - 'Data imported successfully', + AppLocalizations.of(context)!.dataImportedSuccessfully, ); if (mounted) Phoenix.rebirth(context); } else { @@ -85,7 +115,7 @@ class _BackupPageState extends ConsumerState { .map((e) => e.key) .join(', '); - throw Exception('Failed to import some tables: $failedTables'); + throw CsvImportGeneralErrorException(text: 'Failed to import some tables: $failedTables'); } break; @@ -95,13 +125,12 @@ class _BackupPageState extends ConsumerState { ); if(!result) { - throw Exception('Failed to import data from CSV'); + throw CsvImportGeneralErrorException(text: ''); } await CSVFilePicker.showSuccess( context, - 'Data imported successfully', - ); + AppLocalizations.of(context)!.dataImportedSuccessfully); if (mounted) Phoenix.rebirth(context); break; @@ -111,14 +140,13 @@ class _BackupPageState extends ConsumerState { } catch (e) { if (!mounted) return; CSVFilePicker.hideLoading(context); - - showSnackBar(context, message: 'Import failed: ${e.toString()}'); + _showLocalizedError(context, e); } } Future _handleExport() async { try { - CSVFilePicker.showLoading(context, 'Exporting data...'); + CSVFilePicker.showLoading(context, AppLocalizations.of(context)!.exportData); final csv = await SossoldiDatabase.instance.exportToCSV(); @@ -129,28 +157,10 @@ class _BackupPageState extends ConsumerState { } catch (e) { if (!mounted) return; CSVFilePicker.hideLoading(context); - showSnackBar(context, message: 'Export failed: ${e.toString()}'); + showSnackBar(context, message: AppLocalizations.of(context)!.exportFailed(e.toString())); } } - late final List options = [ - BackupOption( - title: 'Import data', - description: 'Import a CSV file to update your database', - icon: Icons.upload_file, - ), - BackupOption( - title: 'Import data', - description: 'Import from CSV from Money Manager\n to update your database\nThe file must be resaved in csv from xls', - icon: Icons.upload_file, - ), - BackupOption( - title: 'Export data', - description: 'Save your data as a CSV file', - icon: Icons.download, - ), - ]; - @override void initState() { super.initState(); @@ -186,13 +196,32 @@ class _BackupPageState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; + + final List options = [ + BackupOption( + title: l10n.importData, + description: l10n.importDataDescription, + icon: Icons.upload_file, + ), + BackupOption( + title: l10n.importData, + description: l10n.importMoneyManager, + icon: Icons.upload_file, + ), + BackupOption( + title: l10n.exportData, + description: l10n.exportDataDescription, + icon: Icons.download, + ), + ]; return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Import/Export'), + title: Text(l10n.importExport), ), body: ListView.separated( padding: const EdgeInsets.only(top: Sizes.xl), diff --git a/lib/pages/settings/general/general_settings_page.dart b/lib/pages/settings/general/general_settings_page.dart index 16f13674..c563f567 100644 --- a/lib/pages/settings/general/general_settings_page.dart +++ b/lib/pages/settings/general/general_settings_page.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/currency.dart'; import '../../../providers/currency_provider.dart'; import '../../../providers/authentication_provider.dart'; @@ -24,6 +25,7 @@ class _GeneralSettingsPageState extends ConsumerState { String selectedCurrency = "EUR"; dynamic selectedLanguage = "🇬🇧"; + // Vorrei non fare commenti riguardo questa lista di liste, ma questo è un commento List> languages = [ ["🇬🇧", "English"], ["🇮🇹", "Italiano"], @@ -33,6 +35,9 @@ class _GeneralSettingsPageState extends ConsumerState { @override Widget build(BuildContext context) { + + var l10n = AppLocalizations.of(context)!; + final appThemeState = ref.watch(appThemeStateProvider); final currencyState = ref.watch(currencyStateProvider); Future> currencyList = ref @@ -46,7 +51,7 @@ class _GeneralSettingsPageState extends ConsumerState { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('General Settings'), + title: Text(l10n.generalSettings), ), body: SingleChildScrollView( padding: const EdgeInsets.only( @@ -61,7 +66,7 @@ class _GeneralSettingsPageState extends ConsumerState { Row( children: [ Text( - "Appearance", + l10n.appearance, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -89,7 +94,7 @@ class _GeneralSettingsPageState extends ConsumerState { Row( children: [ Text( - "Currency", + l10n.currency, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -124,7 +129,7 @@ class _GeneralSettingsPageState extends ConsumerState { Row( children: [ Text( - "Require authentication", + l10n.requireAuthentication, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), diff --git a/lib/pages/settings/general/widgets/currency_selector_dialog.dart b/lib/pages/settings/general/widgets/currency_selector_dialog.dart index 081965f5..c814aa5d 100644 --- a/lib/pages/settings/general/widgets/currency_selector_dialog.dart +++ b/lib/pages/settings/general/widgets/currency_selector_dialog.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../constants/style.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../model/currency.dart'; import '../../../../providers/currency_provider.dart'; @@ -11,11 +12,12 @@ class CurrencySelectorDialog { Currency currency, Future> currencies, ) { + var l10n = AppLocalizations.of(context)!; showDialog( context: context, builder: (context) => AlertDialog( title: Text( - 'Select a currency', + l10n.selectACurrency, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -71,7 +73,7 @@ class CurrencySelectorDialog { }, ); } else if (snapshot.hasError) { - return Text('Something went wrong: ${snapshot.error}'); + return Text(l10n.errorOccurred(snapshot.error.toString())); } else { if (snapshot.connectionState == ConnectionState.waiting) { return Transform.scale( @@ -79,7 +81,7 @@ class CurrencySelectorDialog { child: const CircularProgressIndicator(), ); } else { - return const Text("Search for a transaction"); + return Text(l10n.searchForATransaction); } } }, diff --git a/lib/pages/settings/infos/collaborators_page.dart b/lib/pages/settings/infos/collaborators_page.dart index 2dae3cdc..142c4850 100644 --- a/lib/pages/settings/infos/collaborators_page.dart +++ b/lib/pages/settings/infos/collaborators_page.dart @@ -5,6 +5,7 @@ import 'package:url_launcher/url_launcher.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/device.dart'; import '../../../ui/widgets/default_card.dart'; @@ -47,15 +48,18 @@ IconData _platformIcon(String url) { class CollaboratorsPage extends ConsumerWidget { const CollaboratorsPage({super.key}); + + @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Collaborators'), + title: Text(l10n.collaboratorsTitle), ), body: SingleChildScrollView( padding: const EdgeInsets.only(top: Sizes.xl, bottom: Sizes.xxl), @@ -69,14 +73,14 @@ class CollaboratorsPage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Meet the team', + l10n.meetTheTeam, style: Theme.of(context).textTheme.headlineMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), const SizedBox(height: Sizes.sm), Text( - 'sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.', + l10n.teamDescription, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.outline, ), @@ -120,14 +124,14 @@ class CollaboratorsPage extends ConsumerWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - 'Want to contribute?', + l10n.wantToContribute, style: Theme.of(context).textTheme.titleLarge! .copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - 'Open an issue, submit a PR or just say hi on GitHub', + l10n.contributeDescription, style: Theme.of(context).textTheme.bodySmall! .copyWith( color: Theme.of(context).colorScheme.outline, diff --git a/lib/pages/settings/infos/more_info_page.dart b/lib/pages/settings/infos/more_info_page.dart index fb3577eb..a2a58a4b 100644 --- a/lib/pages/settings/infos/more_info_page.dart +++ b/lib/pages/settings/infos/more_info_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/settings_provider.dart'; import '../../../ui/widgets/default_card.dart'; import '../../../ui/device.dart'; @@ -12,10 +13,11 @@ class MoreInfoPage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final moreInfoOptions = [ - ["App Version:", ref.watch(versionProvider), null], - ["Collaborators", "See the team behind this app", "/collaborators"], - ["Privacy Policy", "Read more", "/privacy-policy"], + [l10n.appVersion, ref.watch(versionProvider), null], + [l10n.collaborators, l10n.collaboratorsDescription, "/collaborators"], + [l10n.privacyPolicy, l10n.privacyPolicyDescription, "/privacy-policy"], ]; return Scaffold( @@ -24,7 +26,7 @@ class MoreInfoPage extends ConsumerWidget { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('App Info'), + title: Text(l10n.appInfo), ), body: ListView.separated( padding: const EdgeInsets.only(top: Sizes.xl), diff --git a/lib/pages/settings/infos/privacy_policy_page.dart b/lib/pages/settings/infos/privacy_policy_page.dart index 0def8b4c..2bdc2a0d 100644 --- a/lib/pages/settings/infos/privacy_policy_page.dart +++ b/lib/pages/settings/infos/privacy_policy_page.dart @@ -5,6 +5,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:flutter/gestures.dart'; +import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/device.dart'; class PrivacyPolicyPage extends ConsumerStatefulWidget { @@ -17,13 +19,15 @@ class PrivacyPolicyPage extends ConsumerStatefulWidget { class _PrivacyPolicyPageState extends ConsumerState { @override Widget build(BuildContext context) { + + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( leading: IconButton( icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Privacy Policy'), + title: Text(l10n.privacyPolicyTitle), ), body: SingleChildScrollView( physics: const BouncingScrollPhysics(), @@ -31,51 +35,50 @@ class _PrivacyPolicyPageState extends ConsumerState { child: Column( children: [ Text( - 'Sossoldi is build as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n', + l10n.privacyIntro, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - 'What Information Do We Collect?\n', + l10n.privacyCollectTitle, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exist solely on your device and no where else.\n", + l10n.privacyCollectBody, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - 'Changes to This Privacy Policy\n', + l10n.privacyChangesTitle, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n', + l10n.privacyChangesBody, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), Text( - 'Contact us\n', + l10n.contactUsTitle, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), RichText( text: TextSpan( - text: - 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at ', + text: l10n.contactUsBody, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), children: [ TextSpan( - text: 'help.sossoldi@gmail.com', + text: '$sossoldiEmail\n\n', style: const TextStyle( color: Colors.blue, decoration: TextDecoration.underline, @@ -85,7 +88,7 @@ class _PrivacyPolicyPageState extends ConsumerState { launchUrl( Uri( scheme: 'mailto', - path: 'help.sossoldi@gmail.com', + path: sossoldiEmail, queryParameters: {'subject': 'Request info'}, ), ); diff --git a/lib/pages/settings/notifications/notifications_settings.dart b/lib/pages/settings/notifications/notifications_settings.dart index 3834b1c1..01fd4959 100644 --- a/lib/pages/settings/notifications/notifications_settings.dart +++ b/lib/pages/settings/notifications/notifications_settings.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/settings_provider.dart'; import '../../../ui/device.dart'; import '../../../services/notifications/notifications_service.dart'; @@ -18,6 +19,7 @@ class NotificationsSettings extends ConsumerWidget { final isTrscAddedReminderEnabled = ref.watch( transactionRecAddedSwitchProvider, ); + var l10n = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( @@ -25,7 +27,7 @@ class NotificationsSettings extends ConsumerWidget { icon: const Icon(Icons.arrow_back_ios_new), onPressed: () => Navigator.pop(context), ), - title: const Text('Notifications'), + title: Text(l10n.notifications), ), body: ListView( padding: const EdgeInsets.only(top: Sizes.xl), @@ -48,7 +50,7 @@ class NotificationsSettings extends ConsumerWidget { children: [ Expanded( child: Text( - "Add transactions reminder", + l10n.addTransactionReminder, style: Theme.of(context).textTheme.bodyMedium, ), ), @@ -98,7 +100,7 @@ class NotificationsSettings extends ConsumerWidget { bottom: Sizes.sm, ), child: Text( - "RECURRING TRANSACTIONS", + l10n.recurringTransactions.toUpperCase(), style: Theme.of( context, ).textTheme.labelLarge!.copyWith(color: grey1), @@ -123,7 +125,7 @@ class NotificationsSettings extends ConsumerWidget { children: [ Expanded( child: Text( - "Recurring transaction added", + l10n.recurringTransactionAdded, style: Theme.of(context).textTheme.bodyMedium, ), ), diff --git a/lib/pages/settings/settings_page.dart b/lib/pages/settings/settings_page.dart index b0a0fb74..f31fc893 100644 --- a/lib/pages/settings/settings_page.dart +++ b/lib/pages/settings/settings_page.dart @@ -10,6 +10,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import '../../constants/constants.dart'; import '../../constants/style.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/widgets/alert_dialog.dart'; import '../../ui/widgets/default_card.dart'; import '../../services/database/sossoldi_database.dart'; @@ -21,46 +22,6 @@ import '../../providers/statistics_provider.dart'; import '../../providers/transactions_provider.dart'; import '../../ui/device.dart'; -var settingsOptions = [ - [ - Icons.settings, - "General Settings", - "Edit general settings", - "/general-settings", - ], - [ - Icons.account_balance_wallet, - "Accounts", - "Add or edit your accounts", - "/account-list", - ], - [ - Icons.list_alt, - "Categories", - "Add/edit categories and subcategories", - "/category-list", - ], - [Icons.attach_money, "Budget", "Add or edit your budgets", null], - [ - Icons.download_for_offline, - "Import/Export", - "Import or export data from a CSV file", - "/backup-page", - ], - [ - Icons.notifications_active, - "Notifications", - "Manage your notifications settings", - "/notifications-settings", - ], - [ - Icons.feedback, - "Leave a feedback", - "Complete a small form to report a bug or leave a feedback", - "https://feedback.sossoldi.com", - ], - [Icons.info, "App Info", "Learn more about us and the app", "/more-info"], -]; class SettingsPage extends ConsumerStatefulWidget { const SettingsPage({super.key}); @@ -114,6 +75,50 @@ class _SettingsPageState extends ConsumerState { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; + + + var settingsOptions = [ + [ + Icons.settings, + l10n.generalSettings, + l10n.generalSettingsDesc, + "/general-settings", + ], + [ + Icons.account_balance_wallet, + l10n.accounts, + l10n.accountsDesc, + "/account-list", + ], + [ + Icons.list_alt, + l10n.categories, + l10n.categoriesDesc, + "/category-list", + ], + [Icons.attach_money, l10n.budget, l10n.budgetDesc, null], + [ + Icons.download_for_offline, + l10n.importExport, + l10n.importExportDesc, + "/backup-page", + ], + [ + Icons.notifications_active, + l10n.notifications, + l10n.notificationsDesc, + "/notifications-settings", + ], + [ + Icons.feedback, + l10n.leaveFeedback,l10n.leaveFeedbackDesc, + "https://feedback.sossoldi.com", + ], + [Icons.info, l10n.appInfo, l10n.appInfoDesc, "/more-info"], + ]; + + return Scaffold( appBar: AppBar( leading: IconButton( @@ -122,7 +127,7 @@ class _SettingsPageState extends ConsumerState { ), title: GestureDetector( onTap: _onSettingsTap, - child: const Text('Settings'), + child: Text(l10n.settings), ), ), body: ListView.builder( @@ -198,7 +203,7 @@ class _SettingsPageState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ Text( - 'Open source, built by the community', + l10n.settingsDisclaimer, style: Theme.of(context).textTheme.bodySmall!.copyWith( color: Theme.of(context).colorScheme.outline, ), diff --git a/lib/pages/structure.dart b/lib/pages/structure.dart index dc3099c9..32e03ba4 100644 --- a/lib/pages/structure.dart +++ b/lib/pages/structure.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../l10n/app_localizations.dart'; import '../providers/settings_provider.dart'; import '../providers/transactions_provider.dart'; import '../ui/device.dart'; @@ -19,14 +20,7 @@ class Structure extends ConsumerStatefulWidget { } class _StructureState extends ConsumerState { - // We could add this List in the app's state, so it isn't intialized every time. - final List _pagesTitle = [ - "Dashboard", - "Transactions", - "", - "Planning", - "Graphs", - ]; + final List _pages = [ const DashboardPage(), const TransactionsPage(), @@ -40,6 +34,16 @@ class _StructureState extends ConsumerState { @override Widget build(BuildContext context) { final isVisible = ref.watch(visibilityAmountProvider); + var l10n = AppLocalizations.of(context)!; + + + List pagesTitle = [ + l10n.dashboard, + l10n.transactions, + "", + l10n.planning, + l10n.graphs, + ]; return Scaffold( // Prevent the fab moving up when the keyboard is opened @@ -50,7 +54,7 @@ class _StructureState extends ConsumerState { : null, title: switch (selectedIndex) { 0 => null, - _ => Text(_pagesTitle.elementAt(selectedIndex)), + _ => Text(pagesTitle.elementAt(selectedIndex)), }, leading: Padding( padding: const EdgeInsets.only(left: Sizes.lg), @@ -92,7 +96,7 @@ class _StructureState extends ConsumerState { items: [ BottomNavigationBarItem( icon: Icon(selectedIndex == 0 ? Icons.home : Icons.home_outlined), - label: "DASHBOARD", + label: AppLocalizations.of(context)!.dashboard.toUpperCase(), ), BottomNavigationBarItem( icon: Icon( @@ -100,7 +104,7 @@ class _StructureState extends ConsumerState { ? Icons.swap_horizontal_circle : Icons.swap_horizontal_circle_outlined, ), - label: "TRANSACTIONS", + label: AppLocalizations.of(context)!.transactions.toUpperCase(), ), const BottomNavigationBarItem(icon: Text(""), label: ""), BottomNavigationBarItem( @@ -109,7 +113,7 @@ class _StructureState extends ConsumerState { ? Icons.calendar_today : Icons.calendar_today_outlined, ), - label: "PLANNING", + label: AppLocalizations.of(context)!.planning.toUpperCase(), ), BottomNavigationBarItem( icon: Icon( @@ -117,7 +121,7 @@ class _StructureState extends ConsumerState { ? Icons.data_exploration : Icons.data_exploration_outlined, ), - label: "GRAPHS", + label: AppLocalizations.of(context)!.graphs.toUpperCase(), ), ], ), diff --git a/lib/pages/transactions/create_transaction/create_transaction_page.dart b/lib/pages/transactions/create_transaction/create_transaction_page.dart index 2538fd30..b50a1bd3 100644 --- a/lib/pages/transactions/create_transaction/create_transaction_page.dart +++ b/lib/pages/transactions/create_transaction/create_transaction_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/transaction.dart'; import '../../../providers/accounts_provider.dart'; import '../../../providers/categories_provider.dart'; @@ -211,8 +212,8 @@ class _CreateTransactionPage extends ConsumerState { appBar: AppBar( title: Text( (widget.transaction != null) - ? "Editing transaction" - : "New transaction", + ? AppLocalizations.of(context)!.editingTransaction + : AppLocalizations.of(context)!.newTransaction, ), actions: [ if (widget.transaction != null) ...[ @@ -269,8 +270,8 @@ class _CreateTransactionPage extends ConsumerState { onPressed: _isSaveEnabled ? _createOrUpdateTransaction : null, child: Text( widget.transaction != null - ? "UPDATE TRANSACTION" - : "ADD TRANSACTION", + ? AppLocalizations.of(context)!.updateTransaction.toUpperCase() + : AppLocalizations.of(context)!.addTransaction.toUpperCase(), ), ), ), @@ -289,7 +290,7 @@ class _CreateTransactionPage extends ConsumerState { bottom: Sizes.sm, ), child: Text( - "DETAILS", + AppLocalizations.of(context)!.details.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -303,7 +304,7 @@ class _CreateTransactionPage extends ConsumerState { const Divider(), if (selectedType != TransactionType.transfer) ...[ DetailsListTile( - title: "Account", + title: AppLocalizations.of(context)!.account, icon: Icons.account_balance_wallet, value: ref.watch(selectedBankAccountProvider)?.name, callback: () { @@ -332,7 +333,7 @@ class _CreateTransactionPage extends ConsumerState { ), const Divider(), DetailsListTile( - title: "Category", + title: AppLocalizations.of(context)!.category, icon: Icons.list_alt, value: ref.watch(selectedCategoryProvider)?.name, callback: () { @@ -362,7 +363,7 @@ class _CreateTransactionPage extends ConsumerState { const Divider(), ], DetailsListTile( - title: "Date", + title: AppLocalizations.of(context)!.date, icon: Icons.calendar_month, value: ref.watch(selectedDateProvider).formatEDMY(), callback: () async { diff --git a/lib/pages/transactions/create_transaction/widgets/account_selector.dart b/lib/pages/transactions/create_transaction/widgets/account_selector.dart index f414321e..b4d7a4b8 100644 --- a/lib/pages/transactions/create_transaction/widgets/account_selector.dart +++ b/lib/pages/transactions/create_transaction/widgets/account_selector.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../constants/constants.dart'; import '../../../../constants/style.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../model/bank_account.dart'; import '../../../../providers/accounts_provider.dart'; @@ -29,14 +30,14 @@ class _AccountSelectorState extends ConsumerState { final accountsList = ref.watch(accountsProvider); final fromAccount = ref.watch(selectedBankAccountProvider); final toAccount = ref.watch(bankAccountTransferProvider); - + var l10n = AppLocalizations.of(context)!; return Container( color: Theme.of(context).colorScheme.primaryContainer, child: Column( mainAxisSize: MainAxisSize.min, children: [ AppBar( - title: const Text("Account"), + title: Text(l10n.account), actions: [ IconButton( onPressed: () { @@ -62,7 +63,7 @@ class _AccountSelectorState extends ConsumerState { bottom: Sizes.sm, ), child: Text( - "MORE FREQUENT", + l10n.moreFrequent.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -138,7 +139,7 @@ class _AccountSelectorState extends ConsumerState { ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ), Container( @@ -149,7 +150,7 @@ class _AccountSelectorState extends ConsumerState { bottom: Sizes.sm, ), child: Text( - "ALL ACCOUNTS", + l10n.allAccounts, style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -203,7 +204,7 @@ class _AccountSelectorState extends ConsumerState { ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(l10n.errorOccurred(err)), ), ], ), diff --git a/lib/pages/transactions/create_transaction/widgets/amount_section.dart b/lib/pages/transactions/create_transaction/widgets/amount_section.dart index d24be168..b1e26aa8 100644 --- a/lib/pages/transactions/create_transaction/widgets/amount_section.dart +++ b/lib/pages/transactions/create_transaction/widgets/amount_section.dart @@ -3,6 +3,7 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import '../../../../constants/constants.dart'; import "../../../../constants/style.dart"; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../model/transaction.dart'; import '../../../../providers/transactions_provider.dart'; @@ -21,7 +22,6 @@ class AmountSection extends ConsumerStatefulWidget { } class _AmountSectionState extends ConsumerState { - static const List _titleList = ['Income', 'Expense', 'Transfer']; List _typeToggleState = [false, true, false]; @@ -42,7 +42,8 @@ class _AmountSectionState extends ConsumerState { Widget build(BuildContext context) { final trsncTypeList = TransactionType.values; final selectedType = ref.watch(selectedTransactionTypeProvider); - + var l10n = AppLocalizations.of(context)!; + List _titleList = [l10n.income, l10n.expense, l10n.transfer]; return Container( color: Theme.of(context).colorScheme.surface, child: Column( @@ -113,7 +114,7 @@ class _AmountSectionState extends ConsumerState { children: [ const SizedBox(height: Sizes.sm), Text( - "FROM:", + l10n.from.toUpperCase(), style: Theme.of(context).textTheme.labelMedium! .copyWith( color: @@ -206,7 +207,7 @@ class _AmountSectionState extends ConsumerState { selectedBankAccountProvider, ) ?.name ?? - "Select Account", + l10n.selectAccount, style: Theme.of(context) .textTheme .bodySmall! @@ -260,7 +261,7 @@ class _AmountSectionState extends ConsumerState { children: [ const SizedBox(height: Sizes.sm), Text( - "TO:", + l10n.to.toUpperCase(), style: Theme.of(context).textTheme.labelMedium! .copyWith( color: @@ -345,7 +346,7 @@ class _AmountSectionState extends ConsumerState { bankAccountTransferProvider, ) ?.name ?? - "Select account", + l10n.selectAccount, style: Theme.of(context) .textTheme .bodySmall! diff --git a/lib/pages/transactions/create_transaction/widgets/category_selector.dart b/lib/pages/transactions/create_transaction/widgets/category_selector.dart index 00f928e7..0ad2a247 100644 --- a/lib/pages/transactions/create_transaction/widgets/category_selector.dart +++ b/lib/pages/transactions/create_transaction/widgets/category_selector.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../../constants/constants.dart'; import '../../../../constants/style.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../model/category_transaction.dart'; import '../../../../providers/categories_provider.dart'; @@ -41,7 +42,7 @@ class _CategorySelectorState extends ConsumerState { mainAxisSize: MainAxisSize.min, children: [ AppBar( - title: const Text("Category"), + title: Text(AppLocalizations.of(context)!.category), actions: [ IconButton( onPressed: () => @@ -64,7 +65,7 @@ class _CategorySelectorState extends ConsumerState { bottom: Sizes.md, ), child: Text( - "MORE FREQUENT", + AppLocalizations.of(context)!.moreFrequent.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -114,7 +115,7 @@ class _CategorySelectorState extends ConsumerState { ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(AppLocalizations.of(context)!.errorOccurred(err)), ), ), Container( @@ -125,7 +126,7 @@ class _CategorySelectorState extends ConsumerState { bottom: Sizes.sm, ), child: Text( - "ALL CATEGORIES", + AppLocalizations.of(context)!.allCategories.toUpperCase(), style: Theme.of(context).textTheme.labelLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -207,7 +208,7 @@ class _CategorySelectorState extends ConsumerState { loading: () => const Center( child: CircularProgressIndicator(), ), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(AppLocalizations.of(context)!.errorOccurred(err)), ), ), ], @@ -217,7 +218,7 @@ class _CategorySelectorState extends ConsumerState { ), loading: () => const Center(child: CircularProgressIndicator()), - error: (err, stack) => Text('Error: $err'), + error: (err, stack) => Text(AppLocalizations.of(context)!.errorOccurred(err)), ), ], ), diff --git a/lib/pages/transactions/create_transaction/widgets/duplicate_transaction_dialog.dart b/lib/pages/transactions/create_transaction/widgets/duplicate_transaction_dialog.dart index eb982f12..093d99ca 100644 --- a/lib/pages/transactions/create_transaction/widgets/duplicate_transaction_dialog.dart +++ b/lib/pages/transactions/create_transaction/widgets/duplicate_transaction_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../model/transaction.dart'; import '../../../../providers/transactions_provider.dart'; import '../../../../ui/device.dart'; @@ -13,16 +14,16 @@ class DuplicateTransactionDialog extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { return AlertDialog( - title: const Text("Duplicate transaction"), - content: const Text( - "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", + title: Text(AppLocalizations.of(context)!.duplicateTransactionTitle), + content: Text( + AppLocalizations.of(context)!.duplicateTransactionContent, ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: const Text("Cancel", style: TextStyle(fontSize: 14)), + child: Text(AppLocalizations.of(context)!.cancel, style: const TextStyle(fontSize: 14)), ), ElevatedButton( style: ElevatedButton.styleFrom( @@ -38,7 +39,7 @@ class DuplicateTransactionDialog extends ConsumerWidget { ..pop(); } }), - child: const Text("Duplicate"), + child: Text(AppLocalizations.of(context)!.duplicate), ), ], ); diff --git a/lib/pages/transactions/create_transaction/widgets/label_list_tile.dart b/lib/pages/transactions/create_transaction/widgets/label_list_tile.dart index 89217ccf..74bb3214 100644 --- a/lib/pages/transactions/create_transaction/widgets/label_list_tile.dart +++ b/lib/pages/transactions/create_transaction/widgets/label_list_tile.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../constants/style.dart'; import '../../../../providers/theme_provider.dart'; @@ -30,7 +31,7 @@ class LabelListTile extends ConsumerWidget { ), const SizedBox(width: Sizes.lg), Text( - "Description", + AppLocalizations.of(context)!.description, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -40,9 +41,9 @@ class LabelListTile extends ConsumerWidget { child: TextField( controller: labelController, textCapitalization: TextCapitalization.sentences, - decoration: const InputDecoration( + decoration: InputDecoration( border: InputBorder.none, - hintText: "Add a description", + hintText: AppLocalizations.of(context)!.addDescription, ), textAlign: TextAlign.end, style: Theme.of(context).textTheme.bodySmall!.copyWith( diff --git a/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile.dart b/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile.dart index eab9526e..be1d717a 100644 --- a/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile.dart +++ b/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../../constants/style.dart"; +import '../../../../l10n/app_localizations.dart'; import '../../../../ui/extensions.dart'; import '../../../../ui/widgets/rounded_icon.dart'; import '../../../../providers/theme_provider.dart'; @@ -39,7 +40,7 @@ class RecurrenceListTile extends ConsumerWidget { backgroundColor: Theme.of(context).colorScheme.secondary, ), title: Text( - "Recurring payment", + AppLocalizations.of(context)!.recurringPayments, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -57,9 +58,9 @@ class RecurrenceListTile extends ConsumerWidget { isSnackBarVisible = true; ScaffoldMessenger.of(context) .showSnackBar( - const SnackBar( - content: Text('Switch is disabled'), - duration: Duration(milliseconds: 800), + SnackBar( + content: Text(AppLocalizations.of(context)!.switchDisabled), + duration: const Duration(milliseconds: 800), ), ) .closed @@ -69,7 +70,7 @@ class RecurrenceListTile extends ConsumerWidget { } }, child: Tooltip( - message: 'Switch is disabled', + message: AppLocalizations.of(context)!.switchDisabled, child: Switch.adaptive( value: isRecurring, onChanged: null, // This makes the switch read-only @@ -105,7 +106,7 @@ class RecurrenceListTile extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "Interval", + AppLocalizations.of(context)!.interval, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -158,14 +159,14 @@ class RecurrenceListTile extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "End repetition", + AppLocalizations.of(context)!.endRepetition, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), const Spacer(), Text( - endDate?.formatEDMY() ?? "Never", + endDate?.formatEDMY() ?? AppLocalizations.of(context)!.never, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: isDarkMode ? grey3 @@ -200,15 +201,15 @@ class RecurrenceListTile extends ConsumerWidget { } }); }, - child: const Row( + child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(Icons.warning, color: Colors.orange), - SizedBox(width: Sizes.sm), + const Icon(Icons.warning, color: Colors.orange), + const SizedBox(width: Sizes.sm), Flexible( child: Text( - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions options, or recurrency options, TAP HERE', - style: TextStyle(color: darkBlue5, fontSize: 13), + AppLocalizations.of(context)!.recurringTransactionWarning, + style: const TextStyle(color: darkBlue5, fontSize: 13), textAlign: TextAlign.center, ), ), diff --git a/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile_edit.dart b/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile_edit.dart index 0f78560c..561da667 100644 --- a/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile_edit.dart +++ b/lib/pages/transactions/create_transaction/widgets/recurrence_list_tile_edit.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import "package:flutter_riverpod/flutter_riverpod.dart"; import "../../../../constants/style.dart"; +import '../../../../l10n/app_localizations.dart'; import '../../../../providers/theme_provider.dart'; import '../../../../providers/transactions_provider.dart'; import '../../../../ui/device.dart'; @@ -36,7 +37,7 @@ class RecurrenceListTileEdit extends ConsumerWidget { ), ), title: Text( - "Recurring payment", + AppLocalizations.of(context)!.recurringPayments, style: Theme.of(context).textTheme.titleLarge!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -61,7 +62,7 @@ class RecurrenceListTileEdit extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "Interval", + AppLocalizations.of(context)!.interval, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), @@ -104,14 +105,14 @@ class RecurrenceListTileEdit extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "End repetition", + AppLocalizations.of(context)!.endRepetition, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: Theme.of(context).colorScheme.primary, ), ), const Spacer(), Text( - endDate != null ? endDate.formatEDMY() : "Never", + endDate != null ? endDate.formatEDMY() : AppLocalizations.of(context)!.never, style: Theme.of(context).textTheme.bodyMedium!.copyWith( color: isDarkMode ? grey3 @@ -151,7 +152,7 @@ class EndDateSelector extends ConsumerWidget { trailing: ref.watch(endDateProvider) != null ? null : const Icon(Icons.check), - title: const Text("Never"), + title: Text(AppLocalizations.of(context)!.never), onTap: () { ref.read(endDateProvider.notifier).setDate(null); Navigator.pop(context); @@ -159,7 +160,7 @@ class EndDateSelector extends ConsumerWidget { ), ListTile( visualDensity: const VisualDensity(vertical: -3), - title: const Text("On a date"), + title: Text(AppLocalizations.of(context)!.onADate), trailing: ref.watch(endDateProvider) != null ? const Icon(Icons.check) : null, diff --git a/lib/pages/transactions/transactions_page.dart b/lib/pages/transactions/transactions_page.dart index 8534c518..15765aa0 100644 --- a/lib/pages/transactions/transactions_page.dart +++ b/lib/pages/transactions/transactions_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import '../../providers/transactions_provider.dart'; import '../../ui/snack_bars/transactions_snack_bars.dart'; import 'widgets/accounts_tab.dart'; @@ -18,12 +19,7 @@ class TransactionsPage extends ConsumerStatefulWidget { class _TransactionsPageState extends ConsumerState with TickerProviderStateMixin { - static const List myTabs = [ - Tab(text: "List", height: 35), - Tab(text: "Categories", height: 35), - Tab(text: "Accounts", height: 35), - ]; - + late List myTabs = []; late TabController _tabController; late ScrollController _scrollController; @@ -33,9 +29,7 @@ class _TransactionsPageState extends ConsumerState @override void initState() { super.initState(); - _tabController = TabController(vsync: this, length: myTabs.length); - // Reset the selected index when switch tab - _tabController.addListener(() => ref.invalidate(selectedListIndexProvider)); + _scrollController = ScrollController(); } @@ -48,6 +42,23 @@ class _TransactionsPageState extends ConsumerState @override Widget build(BuildContext context) { + + final l10n = AppLocalizations.of(context)!; + if(myTabs.isEmpty) + { + myTabs = [ + Tab(text: l10n.list, height: 35), + Tab(text: l10n.categories, height: 35), + Tab(text: l10n.accounts, height: 35), + ]; + _tabController = TabController(vsync: this, length: myTabs.length); + // Reset the selected index when switch tab + _tabController.addListener(() => ref.invalidate(selectedListIndexProvider)); + } + + + _tabController = TabController(vsync: this, length: myTabs.length); + ref.listen( duplicatedTransactionProvider, (prev, curr) => showDuplicatedTransactionSnackBar( diff --git a/lib/pages/transactions/widgets/accounts_pie_chart.dart b/lib/pages/transactions/widgets/accounts_pie_chart.dart index 711fb693..db19086b 100644 --- a/lib/pages/transactions/widgets/accounts_pie_chart.dart +++ b/lib/pages/transactions/widgets/accounts_pie_chart.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/transactions_provider.dart'; import '../../../ui/widgets/rounded_icon.dart'; import '../../../model/bank_account.dart'; @@ -91,7 +92,7 @@ class AccountsPieChart extends ConsumerWidget { ), (selectedIndex != -1) ? Text(accounts[selectedIndex].name) - : const Text("Total"), + : Text(AppLocalizations.of(context)!.total), ], ), ], diff --git a/lib/pages/transactions/widgets/accounts_tab.dart b/lib/pages/transactions/widgets/accounts_tab.dart index 7c0e6c3c..76f1fb77 100644 --- a/lib/pages/transactions/widgets/accounts_tab.dart +++ b/lib/pages/transactions/widgets/accounts_tab.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/widgets/default_container.dart'; import '../../../ui/widgets/transaction_type_button.dart'; import '../../../model/bank_account.dart'; @@ -20,6 +21,7 @@ class AccountsTab extends ConsumerWidget { final accounts = ref.watch(accountsProvider); final transactions = ref.watch(transactionsProvider); final transactionType = ref.watch(selectedTransactionTypeProvider); + var l10n = AppLocalizations.of(context)!; // create a map to link each accounts with a list of its transactions // stored as Map to be passed to AccountListTile @@ -99,10 +101,10 @@ class AccountsTab extends ConsumerWidget { .toList(); return transactionType == TransactionType.income ? accountIncomeList.isEmpty - ? const SizedBox( + ? SizedBox( height: 400, child: Center( - child: Text("No incomes for selected month"), + child: Text(l10n.noIncomesForSelectedMonth), ), ) : AccountSection( @@ -112,10 +114,10 @@ class AccountsTab extends ConsumerWidget { transactions: accountToTransactionsIncome, ) : accountExpenseList.isEmpty - ? const SizedBox( + ? SizedBox( height: 400, child: Center( - child: Text("No expenses for selected month"), + child: Text(l10n.noExpensesForSelectedMonth), ), ) : AccountSection( diff --git a/lib/pages/transactions/widgets/add_transaction_card.dart b/lib/pages/transactions/widgets/add_transaction_card.dart index 909dd75d..7f877616 100644 --- a/lib/pages/transactions/widgets/add_transaction_card.dart +++ b/lib/pages/transactions/widgets/add_transaction_card.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/assets.dart'; import '../../../ui/device.dart'; import '../../../ui/widgets/default_container.dart'; @@ -10,6 +11,7 @@ class AddTransactionCard extends StatelessWidget { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return Align( alignment: Alignment.topCenter, child: DefaultContainer( @@ -22,13 +24,13 @@ class AddTransactionCard extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ Text( - "There are no transactions added yet", + l10n.noTransactionsAdded, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), Image.asset(SossoldiAssets.calculator, width: 240, height: 240), Text( - "Add a transaction to make this section more appealing", + l10n.addTransactionCallToAction, style: Theme.of(context).textTheme.bodySmall, textAlign: TextAlign.center, ), @@ -45,7 +47,7 @@ class AddTransactionCard extends StatelessWidget { size: Sizes.xl, ), label: Text( - "Add transaction", + l10n.addTransaction, style: Theme.of(context).textTheme.titleLarge!.apply( color: Theme.of(context).colorScheme.onPrimaryContainer, ), diff --git a/lib/pages/transactions/widgets/categories_pie_chart.dart b/lib/pages/transactions/widgets/categories_pie_chart.dart index aaedb34d..1816a9aa 100644 --- a/lib/pages/transactions/widgets/categories_pie_chart.dart +++ b/lib/pages/transactions/widgets/categories_pie_chart.dart @@ -4,6 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; import '../../../constants/style.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../providers/transactions_provider.dart'; import '../../../ui/widgets/rounded_icon.dart'; import '../../../model/category_transaction.dart'; @@ -24,6 +25,7 @@ class CategoriesPieChart extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final selectedIndex = ref.watch(selectedListIndexProvider); final selectedCategory = (selectedIndex >= 0) ? categories[selectedIndex] @@ -93,7 +95,7 @@ class CategoriesPieChart extends ConsumerWidget { ), (selectedCategory != null) ? Text(selectedCategory.name) - : const Text("Total"), + : Text(l10n.total), ], ), ], diff --git a/lib/pages/transactions/widgets/categories_tab.dart b/lib/pages/transactions/widgets/categories_tab.dart index 65e7ee05..96cc271f 100644 --- a/lib/pages/transactions/widgets/categories_tab.dart +++ b/lib/pages/transactions/widgets/categories_tab.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../ui/widgets/default_container.dart'; import '../../../ui/widgets/transaction_type_button.dart'; import '../../../model/category_transaction.dart'; @@ -19,6 +20,7 @@ class CategoriesTab extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final categoriesData = ref.watch(categoryWithSubcategoriesDataProvider); final transactionType = ref.watch(selectedTransactionTypeProvider); + var l10n = AppLocalizations.of(context)!; return SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: Sizes.xl), @@ -34,7 +36,7 @@ class CategoriesTab extends ConsumerWidget { height: 400, child: Center( child: Text( - "No ${transactionType == TransactionType.income ? 'incomes' : 'expenses'} for selected month", + transactionType == TransactionType.income ? l10n.noIncomesForSelectedMonth : l10n.noExpensesForSelectedMonth, ), ), ); diff --git a/lib/pages/transactions/widgets/panel_list_tile.dart b/lib/pages/transactions/widgets/panel_list_tile.dart index 9c06849d..ad36284b 100644 --- a/lib/pages/transactions/widgets/panel_list_tile.dart +++ b/lib/pages/transactions/widgets/panel_list_tile.dart @@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:intl/intl.dart'; import '../../../constants/constants.dart'; +import '../../../l10n/app_localizations.dart'; import '../../../model/currency.dart'; import '../../../model/transaction.dart'; import '../../../providers/transactions_provider.dart'; @@ -37,6 +38,7 @@ class PanelListTile extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { final selectedIndex = ref.watch(selectedListIndexProvider); final currency = ref.watch(currencyStateProvider); + var l10n = AppLocalizations.of(context)!; return ClipRRect( borderRadius: BorderRadius.circular(Sizes.borderRadius), child: ExpansionPanelList( @@ -90,7 +92,7 @@ class PanelListTile extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "${transactions.length} transactions", + l10n.transactionCount(transactions.length), style: Theme.of(context).textTheme.labelLarge, ), Text( @@ -126,6 +128,7 @@ class PanelListTile extends ConsumerWidget { List txs, Currency currency, ) { + var l10n = AppLocalizations.of(context)!; final Map> grouped = {}; final List children = []; @@ -145,7 +148,7 @@ class PanelListTile extends ConsumerWidget { : -t.amount.toDouble(); } - final headerName = list.first.categoryName ?? 'Uncategorized'; + final headerName = list.first.categoryName ?? l10n.uncategorized; final percent = list.length * 100 / txs.length; children.add( @@ -200,7 +203,7 @@ class PanelListTile extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - "${list.length} transactions", + l10n.transactionCount(list.length), style: Theme.of(context).textTheme.labelLarge, ), Text( @@ -236,6 +239,7 @@ class TransactionsList extends StatelessWidget { @override Widget build(BuildContext context) { + var l10n = AppLocalizations.of(context)!; return ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), @@ -281,7 +285,7 @@ class TransactionsList extends StatelessWidget { children: [ Text( transaction.categoryName?.toUpperCase() ?? - "Uncategorized", + l10n.uncategorized, style: Theme.of(context).textTheme.labelLarge, ), Text( diff --git a/lib/services/csv/csv_file_picker.dart b/lib/services/csv/csv_file_picker.dart index eef1444b..2f2b3f46 100644 --- a/lib/services/csv/csv_file_picker.dart +++ b/lib/services/csv/csv_file_picker.dart @@ -5,6 +5,7 @@ import 'package:path/path.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import '../../l10n/app_localizations.dart'; import '../../ui/snack_bars/snack_bar.dart'; import '../../ui/device.dart'; @@ -28,7 +29,7 @@ class CSVFilePicker { bool permissionGranted = await _requestStoragePermission(); if (!permissionGranted) { if (context.mounted) { - showSnackBar(context, message: 'Storage permission is required'); + showSnackBar(context, message: AppLocalizations.of(context)!.storagePermissionRequired); } return null; } @@ -45,7 +46,7 @@ class CSVFilePicker { } } catch (e) { if (context.mounted) { - showSnackBar(context, message: 'Error picking file: ${e.toString()}'); + showSnackBar(context, message: AppLocalizations.of(context)!.errorPickingFile(e.toString())); } } return null; @@ -73,14 +74,12 @@ class CSVFilePicker { // Show success message if (context.mounted) { - showSnackBar(context, message: 'File saved to: ${file.path}'); + showSnackBar(context, message: AppLocalizations.of(context)!.fileSavedTo((file.path))); } } catch (e) { if (context.mounted) { - String errorMessage = - 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: ${e.toString()}'; - showSnackBar(context, message: errorMessage); + showSnackBar(context, message: AppLocalizations.of(context)!.saveCsvFileFailed(e.toString())); } } } @@ -135,7 +134,7 @@ class CSVFilePicker { barrierDismissible: false, builder: (BuildContext context) { return AlertDialog( - title: const Text('Success'), + title: Text(AppLocalizations.of(context)!.success), content: Text(message), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(Sizes.borderRadius), @@ -143,7 +142,7 @@ class CSVFilePicker { actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), - child: const Text('OK'), + child: Text(AppLocalizations.of(context)!.ok), ), ], ); diff --git a/lib/services/database/sossoldi_database.dart b/lib/services/database/sossoldi_database.dart index 2e4a3e82..110291fd 100644 --- a/lib/services/database/sossoldi_database.dart +++ b/lib/services/database/sossoldi_database.dart @@ -11,6 +11,7 @@ import 'package:sqflite/sqflite.dart'; // Models import '../../constants/constants.dart'; +import '../../constants/exceptions.dart'; import '../../model/bank_account.dart'; import '../../model/budget.dart'; import '../../model/category_transaction.dart'; @@ -179,7 +180,7 @@ class SossoldiDatabase { allData.add(csvRow); } } catch (e) { - dev.log('Error exporting table $tableName: $e'); + throw CsvExportingErrorException(tableName: '$tableName with error: $e'); } } @@ -196,7 +197,7 @@ class SossoldiDatabase { try { final file = File(csvFilePath); if (!await file.exists()) { - throw Exception('CSV file not found'); + throw CsvNotFoundException(); } final String csvData = await file.readAsString(); @@ -205,7 +206,7 @@ class SossoldiDatabase { ); if (rows.isEmpty) { - throw Exception('CSV file is empty'); + throw CsvEmptyException(); } // First row contains headers @@ -213,7 +214,7 @@ class SossoldiDatabase { final int tableNameIndex = headers.indexOf('table_name'); if (tableNameIndex == -1) { - throw Exception('CSV file missing table_name column'); + throw CsvExpectedColumnException(column: 'table_column'); } // Group rows by table @@ -253,14 +254,12 @@ class SossoldiDatabase { } results[tableName] = true; } catch (e) { - dev.log('Error importing table $tableName: $e'); - results[tableName] = false; + throw CsvImportGeneralErrorException(text : e.toString()); } } }); } catch (e) { - dev.log('Error during import: $e'); - rethrow; + throw CsvImportGeneralErrorException(text : e.toString()); } return results; @@ -313,7 +312,7 @@ class SossoldiDatabase { if(backAccountId == null) { - throw Exception('Error during inserting ${transaction.date} transaction'); + throw CsvTransactionImportErrorException(date: currentDate); } switch(transaction.type) @@ -323,7 +322,7 @@ class SossoldiDatabase { var categoryId = await getCategoryId(txn: txn, type: code, name: transaction.category); if(categoryId == null) { - throw Exception('Error during inserting ${transaction.date} transaction'); + throw CsvTransactionImportErrorException(date: currentDate); } row = { TransactionFields.date: currentDate, @@ -342,7 +341,7 @@ class SossoldiDatabase { var backAccountReceiverId = await getBankAccountId(txn: txn, name: transaction.destinationAccount!); if(backAccountReceiverId == null) { - throw Exception('Error during inserting ${transaction.date} transaction'); + throw CsvTransactionImportErrorException(date: currentDate); } row = { TransactionFields.date : currentDate, @@ -360,6 +359,8 @@ class SossoldiDatabase { } txn.insert('transaction', row); } + + Future insertCategoriesFromMoneyManager({required txn, required Map> categoryMap, required String code, required DateTime oldestDate}) async { List> maps = await txn.query( @@ -428,7 +429,7 @@ class SossoldiDatabase { final file = File(csvFilePath); if (!await file.exists()) { - throw Exception('CSV file not found'); + throw CsvNotFoundException(); } final String csvData = await file.readAsString(); @@ -437,7 +438,7 @@ class SossoldiDatabase { ); if (rows.isEmpty) { - throw Exception('CSV file is empty'); + throw CsvEmptyException(); } // First row contains headers @@ -461,7 +462,7 @@ class SossoldiDatabase { for (var str in expectedHeaders) { if(!headers.contains(str)) { - throw Exception('Column $str not found in CSV file'); + throw CsvExpectedColumnException(column: str); } } @@ -522,7 +523,7 @@ class SossoldiDatabase { accounts.add(cat); break; default: - throw Exception('Income/Expenses column found ${row[6]}, undefined behaviour', ); + throw CsvUnexpectedValueException(value: row[6]); } currencies.add(row[9]); @@ -597,8 +598,7 @@ class SossoldiDatabase { return true; } catch (e) { - dev.log('Error during import: $e'); - rethrow; + throw CsvImportGeneralErrorException(text: e.toString()); } } @@ -775,7 +775,7 @@ class SossoldiDatabase { await batch.commit(); }); } catch (error) { - throw Exception('DbBase.resetDatabase: $error'); + throw ResetDatabaseException(text: error.toString()); } await _createDB(_database!, _migrationManager.latestVersion); } @@ -793,7 +793,7 @@ class SossoldiDatabase { await batch.commit(); }); } catch (error) { - throw Exception('DbBase.cleanDatabase: $error'); + throw CleanDatabaseException(text : error.toString()); } } diff --git a/lib/ui/snack_bars/snack_bar.dart b/lib/ui/snack_bars/snack_bar.dart index 7abeb7e5..bc211d0d 100644 --- a/lib/ui/snack_bars/snack_bar.dart +++ b/lib/ui/snack_bars/snack_bar.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; import '../device.dart'; void showSnackBar( @@ -27,7 +28,7 @@ void showSnackBar( onAction.call(); closeSnackBar(context); }, - child: Text(actionLabel ?? 'Close'), + child: Text(actionLabel ?? AppLocalizations.of(context)!.close), ), ], ), diff --git a/lib/ui/snack_bars/transactions_snack_bars.dart b/lib/ui/snack_bars/transactions_snack_bars.dart index 6ffe9d57..19908244 100644 --- a/lib/ui/snack_bars/transactions_snack_bars.dart +++ b/lib/ui/snack_bars/transactions_snack_bars.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../l10n/app_localizations.dart'; import '../../model/transaction.dart'; import '../../providers/transactions_provider.dart'; import 'snack_bar.dart'; @@ -9,9 +10,11 @@ void showDuplicatedTransactionSnackBar( BuildContext context, { required Transaction? transaction, required WidgetRef ref, -}) => showSnackBar( +}) { + var l10n = AppLocalizations.of(context)!; + return showSnackBar( context, - actionLabel: "Edit", + actionLabel: l10n.edit, onAction: transaction != null ? () async { await ref @@ -23,6 +26,6 @@ void showDuplicatedTransactionSnackBar( } : null, message: transaction != null - ? "${transaction.note} has been created" - : "Error duplicating transaction", -); + ? l10n.transactionCreated(transaction.note.toString()) + : l10n.errorDuplicatingTransaction, +);} diff --git a/lib/ui/widgets/alert_dialog.dart b/lib/ui/widgets/alert_dialog.dart index 989f8557..33e47d35 100644 --- a/lib/ui/widgets/alert_dialog.dart +++ b/lib/ui/widgets/alert_dialog.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import '../../l10n/app_localizations.dart'; import '../theme/app_theme.dart'; /// @@ -79,7 +80,7 @@ class AlertDialogBuilder { void showInfoDialog(BuildContext context, String text) => AlertDialogBuilder( text: text, dialogType: AlertDialogType.info, - primaryActionText: "OK", + primaryActionText: AppLocalizations.of(context)!.ok, ).show(context); /// @@ -88,7 +89,7 @@ void showInfoDialog(BuildContext context, String text) => AlertDialogBuilder( void showSuccessDialog(BuildContext context, String text) => AlertDialogBuilder( text: text, dialogType: AlertDialogType.success, - primaryActionText: "OK", + primaryActionText: AppLocalizations.of(context)!.ok, ).show(context); /// @@ -97,7 +98,7 @@ void showSuccessDialog(BuildContext context, String text) => AlertDialogBuilder( void showWarningDialog(BuildContext context, String text) => AlertDialogBuilder( text: text, dialogType: AlertDialogType.warning, - primaryActionText: "OK", + primaryActionText: AppLocalizations.of(context)!.ok, ).show(context); /// @@ -106,7 +107,7 @@ void showWarningDialog(BuildContext context, String text) => AlertDialogBuilder( void showErrorDialog(BuildContext context, String text) => AlertDialogBuilder( text: text, dialogType: AlertDialogType.error, - primaryActionText: "OK", + primaryActionText: AppLocalizations.of(context)!.ok, ).show(context); enum AlertDialogType { info, success, warning, error } diff --git a/lib/ui/widgets/budget_circular_indicator.dart b/lib/ui/widgets/budget_circular_indicator.dart index 2bd69f26..a27f5eeb 100644 --- a/lib/ui/widgets/budget_circular_indicator.dart +++ b/lib/ui/widgets/budget_circular_indicator.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:percent_indicator/circular_percent_indicator.dart'; +import '../../l10n/app_localizations.dart'; import '../../providers/currency_provider.dart'; import '../device.dart'; import '../extensions.dart'; @@ -59,7 +60,7 @@ class BudgetCircularIndicator extends ConsumerWidget { ), const SizedBox(height: Sizes.sm), Text( - "LEFT", + AppLocalizations.of(context)!.left.toUpperCase(), style: theme.textTheme.labelLarge!.copyWith( color: theme.colorScheme.primary, ), diff --git a/lib/ui/widgets/category_type_button.dart b/lib/ui/widgets/category_type_button.dart index f19151af..3256bf76 100644 --- a/lib/ui/widgets/category_type_button.dart +++ b/lib/ui/widgets/category_type_button.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../constants/style.dart'; +import '../../l10n/app_localizations.dart'; +import '../../l10n/app_localizations_en.dart'; import '../../model/category_transaction.dart'; import '../../providers/categories_provider.dart'; import '../../providers/transactions_provider.dart'; @@ -26,7 +28,7 @@ class CategoryTypeButton extends ConsumerWidget { ref.read(categoryTypeProvider.notifier).setType(type); ref.read(selectedCategoryProvider.notifier).setCategory(null); } - + var l10n = AppLocalizations.of(context)!; return Container( height: 28, decoration: BoxDecoration( @@ -60,7 +62,7 @@ class CategoryTypeButton extends ConsumerWidget { color: Colors.transparent, alignment: Alignment.center, child: Text( - "Income", + l10n.income, style: textStyleFromType(CategoryTransactionType.income), ), ), @@ -75,7 +77,7 @@ class CategoryTypeButton extends ConsumerWidget { color: Colors.transparent, alignment: Alignment.center, child: Text( - 'Expenses', + l10n.expense, style: textStyleFromType(CategoryTransactionType.expense), ), ), diff --git a/lib/ui/widgets/transaction_type_button.dart b/lib/ui/widgets/transaction_type_button.dart index 27a64df2..dbce8949 100644 --- a/lib/ui/widgets/transaction_type_button.dart +++ b/lib/ui/widgets/transaction_type_button.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../constants/style.dart'; +import '../../l10n/app_localizations.dart'; import '../../model/transaction.dart'; import '../../providers/transactions_provider.dart'; import '../device.dart'; @@ -54,7 +55,7 @@ class TransactionTypeButton extends ConsumerWidget { color: Colors.transparent, alignment: Alignment.center, child: Text( - "Income", + AppLocalizations.of(context)!.income, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: (transactionType == TransactionType.income) ? white @@ -77,7 +78,7 @@ class TransactionTypeButton extends ConsumerWidget { color: Colors.transparent, alignment: Alignment.center, child: Text( - 'Expenses', + AppLocalizations.of(context)!.expense, style: Theme.of(context).textTheme.bodyLarge?.copyWith( color: (transactionType == TransactionType.expense) ? white diff --git a/lib/ui/widgets/transactions_list.dart b/lib/ui/widgets/transactions_list.dart index de4377c1..167bcb44 100644 --- a/lib/ui/widgets/transactions_list.dart +++ b/lib/ui/widgets/transactions_list.dart @@ -4,6 +4,7 @@ import 'package:intl/intl.dart'; import '../../constants/constants.dart'; import '../../constants/style.dart'; +import '../../l10n/app_localizations.dart'; import '../../model/transaction.dart'; import '../../providers/currency_provider.dart'; import '../../providers/transactions_provider.dart'; @@ -126,7 +127,7 @@ class _TransactionsListState extends State { child: DefaultContainer( margin: widget.margin, child: Text( - "Add a transaction to make this section more appealing", + AppLocalizations.of(context)!.addTransactionCallToAction, style: Theme.of( context, ).textTheme.bodySmall?.copyWith(fontStyle: FontStyle.italic), @@ -148,6 +149,7 @@ class TransactionTile extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + var l10n = AppLocalizations.of(context)!; final currencyState = ref.watch(currencyStateProvider); return Material( child: ListTile( @@ -192,7 +194,7 @@ class TransactionTile extends ConsumerWidget { subtitle: Text( transaction.type == TransactionType.transfer ? "" - : transaction.categoryName ?? "Uncategorized", + : transaction.categoryName ?? l10n.uncategorized, overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.labelMedium!.copyWith( color: Theme.of(context).colorScheme.primary, diff --git a/pubspec.yaml b/pubspec.yaml index bf4d8769..c6ef7dc6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,7 +23,7 @@ dependencies: flutter_native_splash: ^2.4.7 flutter_phoenix: ^1.1.1 flutter_riverpod: ^3.1.0 - intl: ^0.20.2 + intl: any local_auth: ^3.0.0 package_info_plus: ^9.0.0 path: ^1.9.1 @@ -38,6 +38,8 @@ dependencies: timezone: ^0.10.1 url_launcher: ^6.3.2 font_awesome_flutter: ^10.12.0 + flutter_localizations: + sdk: flutter dev_dependencies: flutter_test: @@ -54,6 +56,7 @@ dev_dependencies: test: ^1.26.3 flutter: + generate: true uses-material-design: true assets: - assets/ From 1dae02160ad9346d6d018b63f83ea221a4f8ded9 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:10:45 +0100 Subject: [PATCH 17/20] Ooops I missed those --- lib/main.dart | 1 - lib/pages/settings/backup/backup_page.dart | 13 +++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 2dedfbb4..f1d9ea42 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -122,7 +122,6 @@ class Launcher extends ConsumerWidget { GlobalCupertinoLocalizations.delegate, AppLocalizations.delegate ], - locale: const Locale('pt', 'PT'), title: 'Sossoldi', diff --git a/lib/pages/settings/backup/backup_page.dart b/lib/pages/settings/backup/backup_page.dart index 8d4ee26a..a2f867a3 100644 --- a/lib/pages/settings/backup/backup_page.dart +++ b/lib/pages/settings/backup/backup_page.dart @@ -115,7 +115,7 @@ class _BackupPageState extends ConsumerState { .map((e) => e.key) .join(', '); - throw CsvImportGeneralErrorException(text: 'Failed to import some tables: $failedTables'); + throw CsvImportGeneralErrorException(text: '$failedTables'); } break; @@ -168,26 +168,27 @@ class _BackupPageState extends ConsumerState { void showImportAlert(context, function) { + var l10n = AppLocalizations.of(context)!; showDialog( context: context, builder: (context) => AlertDialog( - title: const Text('Warning: Data Overwrite'), - content: const Text( - 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.', + title: Text(l10n.warningOverwrite), + content: Text( + l10n.warningOverwriteContent, ), actions: [ TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: const Text('Cancel'), + child: Text(l10n.cancel), ), TextButton( onPressed: () { Navigator.of(context).pop(); function(); }, - child: const Text('Proceed with Import'), + child: Text(l10n.proceedImport), ), ], ), From 73e056e7c0364adbe65cc7bc9d921ffde018caaa Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Mon, 9 Mar 2026 22:33:38 +0100 Subject: [PATCH 18/20] Default to english if a translation is missing --- lib/main.dart | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index f1d9ea42..efd0e198 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -120,8 +120,20 @@ class Launcher extends ConsumerWidget { GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, - AppLocalizations.delegate + AppLocalizations.delegate, ], + localeListResolutionCallback: (locales, supportedLocales) + { + for (var locale in locales ?? []) { + for (var supported in supportedLocales) { + if (supported.languageCode == locale.languageCode) { + return supported; + } + } + } + return const Locale('en'); + }, + title: 'Sossoldi', From 1be879a1ec1a9a81a316422b354283fdd8257bd6 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Tue, 10 Mar 2026 00:17:44 +0100 Subject: [PATCH 19/20] Evaluating Crowdin as community helper --- crowdin.yml | 38 ++ l10n.md | 37 ++ lib/l10n/app_de.arb | 239 +++++++++ lib/l10n/app_en.arb | 17 +- lib/l10n/app_es.arb | 239 +++++++++ lib/l10n/app_it.arb | 38 +- lib/l10n/app_localizations.dart | 54 +- lib/l10n/app_localizations_de.dart | 777 +++++++++++++++++++++++++++++ lib/l10n/app_localizations_en.dart | 8 +- lib/l10n/app_localizations_es.dart | 777 +++++++++++++++++++++++++++++ lib/l10n/app_localizations_it.dart | 26 +- lib/l10n/app_localizations_nl.dart | 777 +++++++++++++++++++++++++++++ lib/l10n/app_localizations_pt.dart | 10 +- lib/l10n/app_nl.arb | 239 +++++++++ lib/l10n/app_pt.arb | 7 +- lib/main.dart | 4 +- 16 files changed, 3200 insertions(+), 87 deletions(-) create mode 100644 crowdin.yml create mode 100644 l10n.md create mode 100644 lib/l10n/app_de.arb create mode 100644 lib/l10n/app_es.arb create mode 100644 lib/l10n/app_localizations_de.dart create mode 100644 lib/l10n/app_localizations_es.dart create mode 100644 lib/l10n/app_localizations_nl.dart create mode 100644 lib/l10n/app_nl.arb diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..9ed3a3ae --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,38 @@ +# +# Basic Crowdin CLI configuration +# See https://crowdin.github.io/crowdin-cli/configuration for more information +# See https://support.crowdin.com/developer/configuration-file/ for all available options +# + +# +# Your Crowdin credentials +# +"project_id": "879202" +"base_path": "." +"base_url": "https://api.crowdin.com" + +# +# Defines whether to preserve the original directory structure in the Crowdin project +# Recommended to set to true +# +"preserve_hierarchy": true + +# +# Files configuration. +# See https://support.crowdin.com/developer/configuration-file/ for all available options +# +files: [ + { + # + # Source files filter + # e.g. "/resources/en/*.json" + # + "source": "/lib/l10n/app_en.arb", + + # + # Translation files filter + # e.g. "/resources/%two_letters_code%/%original_file_name%" + # + "translation": "/lib/l10n/app_%two_letters_code%.arb", + } +] \ No newline at end of file diff --git a/l10n.md b/l10n.md new file mode 100644 index 00000000..def2a858 --- /dev/null +++ b/l10n.md @@ -0,0 +1,37 @@ +# Translation Wiki + +This project uses **Crowdin** to manage localization. Please follow this guide to contribute to translations correctly. + +## Developer Workflow + +Whenever you add new text to the application or before committing, make sure to synchronize translations to avoid conflicts or missing strings. + +### 1. Standard Synchronization +To download the latest translations from Crowdin and update the generated Flutter localization classes, run the following commands in your terminal: + +```bash +# Download updated translations from Crowdin +crowdin download + +# Generate Flutter localization classes +flutter gen-l10n +``` + +### 2. Adding New Keys (Manual Localization) +If you have manually added new keys to the `app_en.arb` source file, follow this procedure to avoid overwriting or conflicts: + +**Point 2 and 3 are only if you forgot to synchronize before adding keys** +1. **Temporary Backup:** Copy the keys you just added to an external text file. +2. **Synchronization:** Run `crowdin download` to align your local project with the latest version from Crowdin. +3. **Restore:** Paste your new keys back into the `app_en.arb` file and save. +4. **Upload Sources:** Push the new source keys to Crowdin so translators can work on them: + ```sh + crowdin upload sources + ``` +5. **Upload Translations:** If you have manually edited any translation files (e.g., app_it.arb), synchronize them with: +```sh + crowdin upload translations +``` + + + diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb new file mode 100644 index 00000000..b1f63d26 --- /dev/null +++ b/lib/l10n/app_de.arb @@ -0,0 +1,239 @@ +{ + "@@locale": "de", + "appName": "Sossoldi", + "@appName": { + "description": "The name of the application" + }, + "dashboard": "Dashboard", + "transactions": "Transactions", + "planning": "Planning", + "graphs": "Graphs", + "list": "List", + "categories": "Categories", + "expenses": "Expenses", + "incomes": "Incomes", + "expense": "Expense", + "income": "Income", + "transfer": "Transfer", + "accounts": "Accounts", + "details": "Details", + "account": "Account", + "category": "Category", + "date": "Date", + "investments": "Investments", + "settings": "Settings", + "notifications": "Notifications", + "settingsDisclaimer": "Open source, built by the community", + "addTransaction": "Add transaction", + "totalBalance": "Total balance", + "netWorth": "Net worth", + "save": "Save", + "cancel": "Cancel", + "success": "Success", + "ok": "Ok", + "editingTransaction": "Editing transaction", + "newTransaction": "New transaction", + "updateTransaction": "Update transaction", + "recurringPayments": "Recurring payments", + "interval": "Interval", + "endRepetition": "End repetition", + "never": "Never", + "onADate": "On a date", + "switchDisabled": "Switch is disabled", + "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", + "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", + "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", + "storagePermissionRequired": "Storage permission is required to access your files.", + "importingData": "Importing data...", + "exportingData": "Exporting data...", + "fileSavedTo": "File saved to: {path}", + "dataImportedSuccessfully": "Data imported successfully", + "description": "Description", + "addDescription": "Add description", + "duplicateTransactionTitle": "Duplicate transaction", + "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", + "duplicate": "Duplicate", + "moreFrequent": "More frequent", + "allCategories": "All categories", + "allAccounts": "All accounts", + "errorOccurred": "Error: {err}", + "selectAccount": "Select Account", + "to": "To:", + "from": "From:", + "recurringTransactionAdded": "Recurring transaction added", + "recurringTransactions": "Recurring transactions", + "addTransactionReminder": "Add transaction reminder", + "privacyPolicyTitle": "Privacy Policy", + "privacyCollectTitle": "What Information Do We Collect?", + "privacyChangesTitle": "Changes to This Privacy Policy", + "contactUsTitle": "Contact us", + "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", + "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", + "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", + "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", + "collaboratorsTitle": "Collaborators", + "meetTheTeam": "Meet the team", + "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", + "wantToContribute": "Want to contribute?", + "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", + "appInfo": "App Info", + "appVersion": "App Version:", + "collaborators": "Collaborators", + "collaboratorsDescription": "See the team behind this app", + "privacyPolicy": "Privacy Policy", + "privacyPolicyDescription": "Read more", + "generalSettings": "General Settings", + "appearance": "Appearance", + "currency": "Currency", + "requireAuthentication": "Require authentication", + "searchForATransaction": "Search for a transaction", + "selectACurrency": "Select a currency", + "search": "Search", + "searchIn": "Search in", + "lastTransactions": "Your last transactions", + "startReconciliation": "Start reconciliation", + "newBalance": "New balance", + "balanceDiscrepancy": "Balance Discrepancy?", + "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", + "newAccount": "New account", + "editAccount": "Edit account", + "createAccount": "Create account", + "accountName": "Account name", + "name": "Name", + "iconAndColor": "Icon and color", + "chooseColor": "Choose color", + "chooseIcon": "Choose icon", + "done": "Fatto", + "add": "Add", + "setAsMainAccount": "Set as main account", + "countsForNetWorth": "Counts for the net worth", + "deleteAccount": "Delete account", + "initialBalance": "Initial balance", + "currentBalance": "Current balance", + "showLess": "Show less", + "showMore": "Show more", + "addSubcategory": "Add subcategory", + "newCategory": "New category", + "editCategory": "Edit category", + "createCategory": "Create category", + "updateCategory": "Update category", + "categoryName": "Category name", + "type": "Type", + "deleteCategory": "Delete category", + "newSubcategory": "New subcategory", + "editSubcategory": "Edit subcategory", + "createSubcategory": "Create subcategory", + "updateSubcategory": "Update subcategory", + "subcategoryName": "Subcategory name", + "deleteSubcategory": "Delete subcategory", + "subcategory": "Subcategory", + "categoryFirstThenBudget": "Add a category first to set a budget", + "inTheNextDays": "In {next} days", + "monthlyBudget": "Monthly budget", + "manage": "Manage", + "swipeLeftToDelete": "Swipe left to delete", + "yourMonthlyBudgetWillBe": "Your monthly budget will be:", + "saveBudget": "Save budget", + "selectCategoriesToCreateBudget": "Select the categories to create your budget", + "amount": "Amount", + "addCategoryBudget": "Add category budget", + "allCategoriesAdded": "You have already added all available categories.", + "delete": "Delete", + "allRecurringPaymentsHere": "All recurring payments will be displayed here", + "addRecurringPayment": "Add recurring payment", + "seeOlderPayments": "See older payments", + "untilDate": "Until {date}", + "olderPayments": "Older payments", + "categoryNotFound": "Category not found", + "back": "Back", + "onTheDay": "- On the {day} day", + "noMonthlyPaymentHistory": "No monthly payment history", + "noRecurrentPaymentHistory": "No recurrent payment history", + "errorLoadingPayments": "Error loading payments: {error}", + "editRecurringTransaction": "Edit recurring transaction", + "detailsExplanation": "Details (any change will affect only future transactions)", + "dateStart": "Date start", + "planned": "Planned", + "composition": "Composition", + "progress": "Progress", + "noBudgetSet": "There are no budgets set", + "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", + "createBudget": "Create budget", + "setUpTheApp": "Set up the app", + "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", + "startTheSetup": "Start the setup", + "budgetAmount": "Budget {amount}€", + "addBudget": "Add budget", + "addBudgetForCategory": "Add budget for category {cat}", + "addCategory": "Add category", + "confirm": "Confirm", + "step1Of2": "Step 1 of 2", + "setupMonthlyBudgets": "Set up your monthly\nbudgets", + "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", + "monthlyBudgetTotal": "Monthly budget total:", + "nextStep": "Next step", + "continueWithoutBudget": "Continue without budget", + "step2Of2": "Step 2 OF 2", + "setLiquidityInMainAccount": "Set the liquidity in your main account", + "addMoreAccounts": "You'll be able to add more accounts within the app.", + "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", + "mainAccount": "Main account", + "setAmount": "Set amount", + "editIconAndColor": "Edit icon and color", + "skipStepOrStartFromZero": "Or you can skip this step and start from 0", + "startTrackingExpenses": "Start tracking your expenses", + "startFromZero": "Start from 0", + "importExport": "Import/Export", + "importData": "Import data", + "importDataDescription": "Import a CSV file to update your database", + "importMoneyManager": "Import from Money Manager", + "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", + "exportData": "Export data", + "exportDataDescription": "Save your data as a CSV file", + "warningOverwrite": "Warning: Data Overwrite", + "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", + "proceedImport": "Proceed with Import", + "importSuccess": "Data imported successfully", + "exportFailed": "Export failed: {err}", + "errorExporting": "Failed to export table: {tableName}", + "errorCsvNotFound": "CSV file not found.", + "errorCsvEmpty": "The CSV file is empty.", + "errorCsvExpectedColumn": "Missing expected column: {column}", + "errorCsvUnexpectedValue": "Found an unexpected value: {value}", + "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", + "errorCsvTransactionImport": "Failed to import transaction on date: {date}", + "errorCleanDatabase": "Failed to clean the database. Reason: {error}", + "errorResetDatabase": "Failed to reset the database. Reason: {error}", + "transactionCount": "{count} transactions", + "uncategorized": "Uncategorized", + "noIncomesForSelectedMonth": "No incomes for the selected month", + "noExpensesForSelectedMonth": "No expenses for the selected month", + "total": "Total", + "noTransactionsAdded": "There are no transactions added yet", + "addTransactionCallToAction": "Add a transaction to make this section more appealing", + "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", + "availableLiquidity": "Available liquidity", + "vsLastMonth": "VS last month", + "monthlyBalance": "Monthly balance", + "currentMonth": "Current month", + "lastMonth": "Last month", + "yourAccounts": "Your accounts", + "yourBudgets": "Your budgets", + "createBudgetToTrack": "Create a budget to track your spending", + "close": "Close", + "edit": "Edit", + "errorDuplicatingTransaction": "Error duplicating transaction", + "transactionCreated": "\"{transaction}\" has been created", + "left": "Left", + "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", + "generalSettingsDesc": "Edit general settings", + "accountsDesc": "Add or edit your accounts", + "categoriesDesc": "Add/edit categories and subcategories", + "budget": "Budget", + "budgetDesc": "Add or edit your budgets", + "importExportDesc": "Import or export data", + "notificationsDesc": "Manage your notifications settings", + "leaveFeedback": "Leave a feedback", + "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", + "appInfoDesc": "Learn more about us and the app" +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f8a479c1..c9013a55 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -30,7 +30,7 @@ "save": "Save", "cancel": "Cancel", "success": "Success", - "ok" : "Ok", + "ok": "Ok", "editingTransaction": "Editing transaction", "newTransaction": "New transaction", "updateTransaction": "Update transaction", @@ -129,7 +129,6 @@ "subcategory": "Subcategory", "categoryFirstThenBudget": "Add a category first to set a budget", "inTheNextDays": "In {next} days", - "monthlyBudget": "Monthly budget", "manage": "Manage", "swipeLeftToDelete": "Swipe left to delete", @@ -168,14 +167,12 @@ "addBudgetForCategory": "Add budget for category {cat}", "addCategory": "Add category", "confirm": "Confirm", - "step1Of2": "Step 1 of 2", "setupMonthlyBudgets": "Set up your monthly\nbudgets", "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", "monthlyBudgetTotal": "Monthly budget total:", "nextStep": "Next step", "continueWithoutBudget": "Continue without budget", - "step2Of2": "Step 2 OF 2", "setLiquidityInMainAccount": "Set the liquidity in your main account", "addMoreAccounts": "You'll be able to add more accounts within the app.", @@ -186,8 +183,6 @@ "skipStepOrStartFromZero": "Or you can skip this step and start from 0", "startTrackingExpenses": "Start tracking your expenses", "startFromZero": "Start from 0", - - "importExport": "Import/Export", "importData": "Import data", "importDataDescription": "Import a CSV file to update your database", @@ -197,12 +192,9 @@ "exportDataDescription": "Save your data as a CSV file", "warningOverwrite": "Warning: Data Overwrite", "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", - "proceedImport": "Proceed with Import", "importSuccess": "Data imported successfully", "exportFailed": "Export failed: {err}", - - "errorExporting": "Failed to export table: {tableName}", "errorCsvNotFound": "CSV file not found.", "errorCsvEmpty": "The CSV file is empty.", @@ -212,9 +204,6 @@ "errorCsvTransactionImport": "Failed to import transaction on date: {date}", "errorCleanDatabase": "Failed to clean the database. Reason: {error}", "errorResetDatabase": "Failed to reset the database. Reason: {error}", - - - "transactionCount": "{count} transactions", "uncategorized": "Uncategorized", "noIncomesForSelectedMonth": "No incomes for the selected month", @@ -222,18 +211,15 @@ "total": "Total", "noTransactionsAdded": "There are no transactions added yet", "addTransactionCallToAction": "Add a transaction to make this section more appealing", - "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", "availableLiquidity": "Available liquidity", "vsLastMonth": "VS last month", - "monthlyBalance": "Monthly balance", "currentMonth": "Current month", "lastMonth": "Last month", "yourAccounts": "Your accounts", "yourBudgets": "Your budgets", "createBudgetToTrack": "Create a budget to track your spending", - "close": "Close", "edit": "Edit", "errorDuplicatingTransaction": "Error duplicating transaction", @@ -250,5 +236,4 @@ "leaveFeedback": "Leave a feedback", "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", "appInfoDesc": "Learn more about us and the app" - } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb new file mode 100644 index 00000000..45c50c14 --- /dev/null +++ b/lib/l10n/app_es.arb @@ -0,0 +1,239 @@ +{ + "@@locale": "es", + "appName": "Sossoldi", + "@appName": { + "description": "The name of the application" + }, + "dashboard": "Dashboard", + "transactions": "Transactions", + "planning": "Planning", + "graphs": "Graphs", + "list": "List", + "categories": "Categories", + "expenses": "Expenses", + "incomes": "Incomes", + "expense": "Expense", + "income": "Income", + "transfer": "Transfer", + "accounts": "Accounts", + "details": "Details", + "account": "Account", + "category": "Category", + "date": "Date", + "investments": "Investments", + "settings": "Settings", + "notifications": "Notifications", + "settingsDisclaimer": "Open source, built by the community", + "addTransaction": "Add transaction", + "totalBalance": "Total balance", + "netWorth": "Net worth", + "save": "Save", + "cancel": "Cancel", + "success": "Success", + "ok": "Ok", + "editingTransaction": "Editing transaction", + "newTransaction": "New transaction", + "updateTransaction": "Update transaction", + "recurringPayments": "Recurring payments", + "interval": "Interval", + "endRepetition": "End repetition", + "never": "Never", + "onADate": "On a date", + "switchDisabled": "Switch is disabled", + "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", + "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", + "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", + "storagePermissionRequired": "Storage permission is required to access your files.", + "importingData": "Importing data...", + "exportingData": "Exporting data...", + "fileSavedTo": "File saved to: {path}", + "dataImportedSuccessfully": "Data imported successfully", + "description": "Description", + "addDescription": "Add description", + "duplicateTransactionTitle": "Duplicate transaction", + "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", + "duplicate": "Duplicate", + "moreFrequent": "More frequent", + "allCategories": "All categories", + "allAccounts": "All accounts", + "errorOccurred": "Error: {err}", + "selectAccount": "Select Account", + "to": "To:", + "from": "From:", + "recurringTransactionAdded": "Recurring transaction added", + "recurringTransactions": "Recurring transactions", + "addTransactionReminder": "Add transaction reminder", + "privacyPolicyTitle": "Privacy Policy", + "privacyCollectTitle": "What Information Do We Collect?", + "privacyChangesTitle": "Changes to This Privacy Policy", + "contactUsTitle": "Contact us", + "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", + "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", + "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", + "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", + "collaboratorsTitle": "Collaborators", + "meetTheTeam": "Meet the team", + "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", + "wantToContribute": "Want to contribute?", + "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", + "appInfo": "App Info", + "appVersion": "App Version:", + "collaborators": "Collaborators", + "collaboratorsDescription": "See the team behind this app", + "privacyPolicy": "Privacy Policy", + "privacyPolicyDescription": "Read more", + "generalSettings": "General Settings", + "appearance": "Appearance", + "currency": "Currency", + "requireAuthentication": "Require authentication", + "searchForATransaction": "Search for a transaction", + "selectACurrency": "Select a currency", + "search": "Search", + "searchIn": "Search in", + "lastTransactions": "Your last transactions", + "startReconciliation": "Start reconciliation", + "newBalance": "New balance", + "balanceDiscrepancy": "Balance Discrepancy?", + "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", + "newAccount": "New account", + "editAccount": "Edit account", + "createAccount": "Create account", + "accountName": "Account name", + "name": "Name", + "iconAndColor": "Icon and color", + "chooseColor": "Choose color", + "chooseIcon": "Choose icon", + "done": "Fatto", + "add": "Add", + "setAsMainAccount": "Set as main account", + "countsForNetWorth": "Counts for the net worth", + "deleteAccount": "Delete account", + "initialBalance": "Initial balance", + "currentBalance": "Current balance", + "showLess": "Show less", + "showMore": "Show more", + "addSubcategory": "Add subcategory", + "newCategory": "New category", + "editCategory": "Edit category", + "createCategory": "Create category", + "updateCategory": "Update category", + "categoryName": "Category name", + "type": "Type", + "deleteCategory": "Delete category", + "newSubcategory": "New subcategory", + "editSubcategory": "Edit subcategory", + "createSubcategory": "Create subcategory", + "updateSubcategory": "Update subcategory", + "subcategoryName": "Subcategory name", + "deleteSubcategory": "Delete subcategory", + "subcategory": "Subcategory", + "categoryFirstThenBudget": "Add a category first to set a budget", + "inTheNextDays": "In {next} days", + "monthlyBudget": "Monthly budget", + "manage": "Manage", + "swipeLeftToDelete": "Swipe left to delete", + "yourMonthlyBudgetWillBe": "Your monthly budget will be:", + "saveBudget": "Save budget", + "selectCategoriesToCreateBudget": "Select the categories to create your budget", + "amount": "Amount", + "addCategoryBudget": "Add category budget", + "allCategoriesAdded": "You have already added all available categories.", + "delete": "Delete", + "allRecurringPaymentsHere": "All recurring payments will be displayed here", + "addRecurringPayment": "Add recurring payment", + "seeOlderPayments": "See older payments", + "untilDate": "Until {date}", + "olderPayments": "Older payments", + "categoryNotFound": "Category not found", + "back": "Back", + "onTheDay": "- On the {day} day", + "noMonthlyPaymentHistory": "No monthly payment history", + "noRecurrentPaymentHistory": "No recurrent payment history", + "errorLoadingPayments": "Error loading payments: {error}", + "editRecurringTransaction": "Edit recurring transaction", + "detailsExplanation": "Details (any change will affect only future transactions)", + "dateStart": "Date start", + "planned": "Planned", + "composition": "Composition", + "progress": "Progress", + "noBudgetSet": "There are no budgets set", + "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", + "createBudget": "Create budget", + "setUpTheApp": "Set up the app", + "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", + "startTheSetup": "Start the setup", + "budgetAmount": "Budget {amount}€", + "addBudget": "Add budget", + "addBudgetForCategory": "Add budget for category {cat}", + "addCategory": "Add category", + "confirm": "Confirm", + "step1Of2": "Step 1 of 2", + "setupMonthlyBudgets": "Set up your monthly\nbudgets", + "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", + "monthlyBudgetTotal": "Monthly budget total:", + "nextStep": "Next step", + "continueWithoutBudget": "Continue without budget", + "step2Of2": "Step 2 OF 2", + "setLiquidityInMainAccount": "Set the liquidity in your main account", + "addMoreAccounts": "You'll be able to add more accounts within the app.", + "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", + "mainAccount": "Main account", + "setAmount": "Set amount", + "editIconAndColor": "Edit icon and color", + "skipStepOrStartFromZero": "Or you can skip this step and start from 0", + "startTrackingExpenses": "Start tracking your expenses", + "startFromZero": "Start from 0", + "importExport": "Import/Export", + "importData": "Import data", + "importDataDescription": "Import a CSV file to update your database", + "importMoneyManager": "Import from Money Manager", + "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", + "exportData": "Export data", + "exportDataDescription": "Save your data as a CSV file", + "warningOverwrite": "Warning: Data Overwrite", + "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", + "proceedImport": "Proceed with Import", + "importSuccess": "Data imported successfully", + "exportFailed": "Export failed: {err}", + "errorExporting": "Failed to export table: {tableName}", + "errorCsvNotFound": "CSV file not found.", + "errorCsvEmpty": "The CSV file is empty.", + "errorCsvExpectedColumn": "Missing expected column: {column}", + "errorCsvUnexpectedValue": "Found an unexpected value: {value}", + "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", + "errorCsvTransactionImport": "Failed to import transaction on date: {date}", + "errorCleanDatabase": "Failed to clean the database. Reason: {error}", + "errorResetDatabase": "Failed to reset the database. Reason: {error}", + "transactionCount": "{count} transactions", + "uncategorized": "Uncategorized", + "noIncomesForSelectedMonth": "No incomes for the selected month", + "noExpensesForSelectedMonth": "No expenses for the selected month", + "total": "Total", + "noTransactionsAdded": "There are no transactions added yet", + "addTransactionCallToAction": "Add a transaction to make this section more appealing", + "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", + "availableLiquidity": "Available liquidity", + "vsLastMonth": "VS last month", + "monthlyBalance": "Monthly balance", + "currentMonth": "Current month", + "lastMonth": "Last month", + "yourAccounts": "Your accounts", + "yourBudgets": "Your budgets", + "createBudgetToTrack": "Create a budget to track your spending", + "close": "Close", + "edit": "Edit", + "errorDuplicatingTransaction": "Error duplicating transaction", + "transactionCreated": "\"{transaction}\" has been created", + "left": "Left", + "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", + "generalSettingsDesc": "Edit general settings", + "accountsDesc": "Add or edit your accounts", + "categoriesDesc": "Add/edit categories and subcategories", + "budget": "Budget", + "budgetDesc": "Add or edit your budgets", + "importExportDesc": "Import or export data", + "notificationsDesc": "Manage your notifications settings", + "leaveFeedback": "Leave a feedback", + "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", + "appInfoDesc": "Learn more about us and the app" +} \ No newline at end of file diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index b03ead95..e2216de2 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -2,7 +2,7 @@ "@@locale": "it", "appName": "Sossoldi", "@appName": { - "description": "Il nome dell'applicazione" + "description": "The name of the application" }, "dashboard": "Dashboard", "transactions": "Transazioni", @@ -30,7 +30,7 @@ "save": "Salva", "cancel": "Annulla", "success": "Successo", - "ok" : "Ok", + "ok": "Ok", "editingTransaction": "Modifica transazione", "newTransaction": "Nuova transazione", "updateTransaction": "Modifica transazione", @@ -40,6 +40,7 @@ "never": "Mai", "onADate": "In data", "switchDisabled": "Gesto disabilitato", + "recurringTransactionWarning": "Questa è una transazione generata da una ricorrente: qualsiasi modifica influenzerà questa transazione unica.\nPer modificare tutte le transazioni future, o le opzioni di ricorrenza, TAP QUI.", "saveCsvFileFailed": "Non puoi salvare i file qui, crea o seleziona una cartella in Downloads o Documenti. Errore: ${e}", "errorPickingFile": "Errore durante la selezione del file. Assicurati di avere i permessi necessari. Errore: {error}", "storagePermissionRequired": "È richiesto il permesso di archiviazione per accedere ai file.", @@ -49,7 +50,6 @@ "dataImportedSuccessfully": "Dati importati con successo", "description": "Descrizione", "addDescription": "Aggiungi descrizione", - "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", "duplicateTransactionTitle": "Transazione duplicata", "duplicateTransactionContent": "Questa transazione è già presente nella lista. Vuoi duplicarla? Potrai poi modificare il nuovo inserimento.", "duplicate": "Duplica", @@ -63,14 +63,14 @@ "recurringTransactionAdded": "Transazione ricorrente aggiunta", "recurringTransactions": "Transazioni ricorrenti", "addTransactionReminder": "Aggiungi promemoria transazione", - "privacyPolicyTitle": "Privacy Policy", + "privacyPolicyTitle": "Politica Sulla Privacy", "privacyCollectTitle": "Quali informazioni raccogliamo?", - "privacyChangesTitle": "Modifiche alla Privacy Policy", + "privacyChangesTitle": "Modifiche alla politica sulla privacy", "contactUsTitle": "Contattaci", - "privacyIntro": "Sossoldi è sviluppata come un'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n", + "privacyIntro": "Sossoldi è sviluppata come app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n", "privacyCollectBody": "Sossoldi non raccoglie alcuna informazione personale e non si connette a Internet. Qualsiasi informazione aggiunta nell'app esiste esclusivamente sul tuo dispositivo e da nessun'altra parte.\n", - "privacyChangesBody": "Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n", - "contactUsBody": "Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all'indirizzo\n", + "privacyChangesBody": "Potremmo aggiornare la nostra politica sulla privacy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta politica è efficace dal 01-01-2024.\n", + "contactUsBody": "Se hai domande o suggerimenti sulla nostra politica sulla privacy, non esitare a contattarci all'indirizzo\n", "collaboratorsTitle": "Collaboratori", "meetTheTeam": "Incontra il team", "teamDescription": "Sossoldi è sviluppata e mantenuta da una appassionata community open source. Ogni funzione, correzione e idea arriva da persone come te.", @@ -80,7 +80,7 @@ "appVersion": "Versione app:", "collaborators": "Collaboratori", "collaboratorsDescription": "Scopri il team dietro questa app", - "privacyPolicy": "Privacy Policy", + "privacyPolicy": "Politica Sulla Privacy", "privacyPolicyDescription": "Leggi di più", "generalSettings": "Impostazioni generali", "appearance": "Aspetto", @@ -129,8 +129,6 @@ "subcategory": "Sottocagegoria", "categoryFirstThenBudget": "Aggiungi una categoria prima di creare un budget", "inTheNextDays": "In {next} giorni", - - "monthlyBudget": "Budget mensile", "manage": "Gestisci", "swipeLeftToDelete": "Scorri a sinistra per eliminare", @@ -164,22 +162,20 @@ "setUpTheApp": "Configura l'app", "setupDescription": "In pochi passaggi sarai pronto a iniziare a tenere\ntraccia delle tue finanze personali (quasi) come\nMr. Rip.", "startTheSetup": "Inizia la configurazione", - "budgetAmount": "Budget {amount}€", + "budgetAmount": "Bilancio {amount}€", "addBudget": "Aggiungi budget", "addBudgetForCategory": "Aggiungi un budget per la categoria {cat}", "addCategory": "Aggiungi categoria", "confirm": "Conferma", - "step1Of2": "Passaggio 1 di 2", "setupMonthlyBudgets": "Imposta i tuoi budget\nmensili", "chooseCategoriesForBudget": "Scegli le categorie per le quali vuoi impostare un budget", "monthlyBudgetTotal": "Totale budget mensile:", "nextStep": "Passaggio successivo", "continueWithoutBudget": "Continua senza budget", - "step2Of2": "Passaggio 2 DI 2", "setLiquidityInMainAccount": "Imposta la liquidità nel tuo conto principale", - "addMoreAccounts": "Sarai in grado di aggiungere altri account dall'app", + "addMoreAccounts": "Sarai in grado di aggiungere altri account dall'app.", "liquidityDescription": "Verrà utilizzata come base a cui aggiungere entrate, spese e calcolare il tuo patrimonio.\nPotrai aggiungere altri conti all'interno dell'app.", "mainAccount": "Conto principale", "setAmount": "Imposta importo", @@ -187,7 +183,6 @@ "skipStepOrStartFromZero": "Oppure puoi saltare questo passaggio e iniziare da 0", "startTrackingExpenses": "Inizia a tracciare le tue spese", "startFromZero": "Inizia da 0", - "importExport": "Importa/Esporta", "importData": "Importa dati", "importDataDescription": "Importa un file CSV per aggiornare il database", @@ -197,12 +192,9 @@ "exportDataDescription": "Salva i tuoi dati come file CSV", "warningOverwrite": "Attenzione: Sovrascrittura dati", "warningOverwriteContent": "L'importazione di questo file sostituirà definitivamente i tuoi dati esistenti. Questa azione non può essere annullata. Assicurati di avere un backup prima di procedere.", - "proceedImport": "Procedi con l'importazione", "importSuccess": "Dati importati con successo", "exportFailed": "Esportazione fallita: {err}", - - "errorExporting": "Impossibile esportare la tabella: {tableName}", "errorCsvNotFound": "File CSV non trovato.", "errorCsvEmpty": "Il file CSV è vuoto.", @@ -212,7 +204,6 @@ "errorCsvTransactionImport": "Errore durante l'importazione della transazione in data: {date}", "errorCleanDatabase": "Impossibile pulire il database. Motivo: {error}", "errorResetDatabase": "Impossibile ripristinare il database. Motivo: {error}", - "transactionCount": "{count} transazioni", "uncategorized": "Senza categoria", "noIncomesForSelectedMonth": "Nessuna entrata per il mese selezionato", @@ -220,34 +211,29 @@ "total": "Totale", "noTransactionsAdded": "Non ci sono ancora transazioni aggiunte", "addTransactionCallToAction": "Aggiungi una transazione per rendere questa sezione più interessante", - "graphsEmptyState": "Dopo aver aggiunto alcune transazioni, dei grafici eccezionali appariranno qui... quasi per magia!", "availableLiquidity": "Liquidità disponibile", "vsLastMonth": "VS mese scorso", - "monthlyBalance": "Bilancio mensile", "currentMonth": "Mese corrente", "lastMonth": "Mese scorso", "yourAccounts": "I tuoi conti", "yourBudgets": "I tuoi budget", "createBudgetToTrack": "Crea un budget per monitorare le tue spese", - "close": "Chiudi", "edit": "Modifica", "errorDuplicatingTransaction": "Errore durante la duplicazione della transazione", "transactionCreated": "\"{transaction}\" è stata creata", "left": "Rimanenti", "notEnoughDataForGraph": "Siamo spiacenti, ma non ci sono\nabbastanza dati per creare il grafico...", - "generalSettingsDesc": "Modifica impostazioni generali", "accountsDesc": "Aggiungi o modifica i tuoi conti", "categoriesDesc": "Aggiungi/modifica categorie e sottocategorie", - "budget": "Budget", + "budget": "Bilancio", "budgetDesc": "Aggiungi o modifica i tuoi budget", "importExportDesc": "Importa o esporta dati", "notificationsDesc": "Gestisci le impostazioni delle notifiche", "leaveFeedback": "Lascia un feedback", "leaveFeedbackDesc": "Compila un modulo per segnalare un bug o lasciare un feedback", "appInfoDesc": "Scopri di più su di noi e sull'app" - } \ No newline at end of file diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 69cd8d69..dada9534 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -5,8 +5,11 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:intl/intl.dart' as intl; +import 'app_localizations_de.dart'; import 'app_localizations_en.dart'; +import 'app_localizations_es.dart'; import 'app_localizations_it.dart'; +import 'app_localizations_nl.dart'; import 'app_localizations_pt.dart'; // ignore_for_file: type=lint @@ -95,12 +98,15 @@ abstract class AppLocalizations { /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ + Locale('de'), Locale('en'), + Locale('es'), Locale('it'), + Locale('nl'), Locale('pt'), ]; - /// Il nome dell'applicazione + /// The name of the application /// /// In it, this message translates to: /// **'Sossoldi'** @@ -322,6 +328,12 @@ abstract class AppLocalizations { /// **'Gesto disabilitato'** String get switchDisabled; + /// No description provided for @recurringTransactionWarning. + /// + /// In it, this message translates to: + /// **'Questa è una transazione generata da una ricorrente: qualsiasi modifica influenzerà questa transazione unica.\nPer modificare tutte le transazioni future, o le opzioni di ricorrenza, TAP QUI.'** + String get recurringTransactionWarning; + /// No description provided for @saveCsvFileFailed. /// /// In it, this message translates to: @@ -376,12 +388,6 @@ abstract class AppLocalizations { /// **'Aggiungi descrizione'** String get addDescription; - /// No description provided for @recurringTransactionWarning. - /// - /// In it, this message translates to: - /// **'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'** - String get recurringTransactionWarning; - /// No description provided for @duplicateTransactionTitle. /// /// In it, this message translates to: @@ -463,7 +469,7 @@ abstract class AppLocalizations { /// No description provided for @privacyPolicyTitle. /// /// In it, this message translates to: - /// **'Privacy Policy'** + /// **'Politica Sulla Privacy'** String get privacyPolicyTitle; /// No description provided for @privacyCollectTitle. @@ -475,7 +481,7 @@ abstract class AppLocalizations { /// No description provided for @privacyChangesTitle. /// /// In it, this message translates to: - /// **'Modifiche alla Privacy Policy'** + /// **'Modifiche alla politica sulla privacy'** String get privacyChangesTitle; /// No description provided for @contactUsTitle. @@ -487,7 +493,7 @@ abstract class AppLocalizations { /// No description provided for @privacyIntro. /// /// In it, this message translates to: - /// **'Sossoldi è sviluppata come un\'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'** + /// **'Sossoldi è sviluppata come app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'** String get privacyIntro; /// No description provided for @privacyCollectBody. @@ -499,13 +505,13 @@ abstract class AppLocalizations { /// No description provided for @privacyChangesBody. /// /// In it, this message translates to: - /// **'Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n'** + /// **'Potremmo aggiornare la nostra politica sulla privacy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta politica è efficace dal 01-01-2024.\n'** String get privacyChangesBody; /// No description provided for @contactUsBody. /// /// In it, this message translates to: - /// **'Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all\'indirizzo\n'** + /// **'Se hai domande o suggerimenti sulla nostra politica sulla privacy, non esitare a contattarci all\'indirizzo\n'** String get contactUsBody; /// No description provided for @collaboratorsTitle. @@ -565,7 +571,7 @@ abstract class AppLocalizations { /// No description provided for @privacyPolicy. /// /// In it, this message translates to: - /// **'Privacy Policy'** + /// **'Politica Sulla Privacy'** String get privacyPolicy; /// No description provided for @privacyPolicyDescription. @@ -1057,7 +1063,7 @@ abstract class AppLocalizations { /// No description provided for @budgetAmount. /// /// In it, this message translates to: - /// **'Budget {amount}€'** + /// **'Bilancio {amount}€'** String budgetAmount(Object amount); /// No description provided for @addBudget. @@ -1135,7 +1141,7 @@ abstract class AppLocalizations { /// No description provided for @addMoreAccounts. /// /// In it, this message translates to: - /// **'Sarai in grado di aggiungere altri account dall\'app'** + /// **'Sarai in grado di aggiungere altri account dall\'app.'** String get addMoreAccounts; /// No description provided for @liquidityDescription. @@ -1459,7 +1465,7 @@ abstract class AppLocalizations { /// No description provided for @budget. /// /// In it, this message translates to: - /// **'Budget'** + /// **'Bilancio'** String get budget; /// No description provided for @budgetDesc. @@ -1509,8 +1515,14 @@ class _AppLocalizationsDelegate } @override - bool isSupported(Locale locale) => - ['en', 'it', 'pt'].contains(locale.languageCode); + bool isSupported(Locale locale) => [ + 'de', + 'en', + 'es', + 'it', + 'nl', + 'pt', + ].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; @@ -1519,10 +1531,16 @@ class _AppLocalizationsDelegate AppLocalizations lookupAppLocalizations(Locale locale) { // Lookup logic when only language code is specified. switch (locale.languageCode) { + case 'de': + return AppLocalizationsDe(); case 'en': return AppLocalizationsEn(); + case 'es': + return AppLocalizationsEs(); case 'it': return AppLocalizationsIt(); + case 'nl': + return AppLocalizationsNl(); case 'pt': return AppLocalizationsPt(); } diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart new file mode 100644 index 00000000..291b3053 --- /dev/null +++ b/lib/l10n/app_localizations_de.dart @@ -0,0 +1,777 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class AppLocalizationsDe extends AppLocalizations { + AppLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transactions'; + + @override + String get planning => 'Planning'; + + @override + String get graphs => 'Graphs'; + + @override + String get list => 'List'; + + @override + String get categories => 'Categories'; + + @override + String get expenses => 'Expenses'; + + @override + String get incomes => 'Incomes'; + + @override + String get expense => 'Expense'; + + @override + String get income => 'Income'; + + @override + String get transfer => 'Transfer'; + + @override + String get accounts => 'Accounts'; + + @override + String get details => 'Details'; + + @override + String get account => 'Account'; + + @override + String get category => 'Category'; + + @override + String get date => 'Date'; + + @override + String get investments => 'Investments'; + + @override + String get settings => 'Settings'; + + @override + String get notifications => 'Notifications'; + + @override + String get settingsDisclaimer => 'Open source, built by the community'; + + @override + String get addTransaction => 'Add transaction'; + + @override + String get totalBalance => 'Total balance'; + + @override + String get netWorth => 'Net worth'; + + @override + String get save => 'Save'; + + @override + String get cancel => 'Cancel'; + + @override + String get success => 'Success'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Editing transaction'; + + @override + String get newTransaction => 'New transaction'; + + @override + String get updateTransaction => 'Update transaction'; + + @override + String get recurringPayments => 'Recurring payments'; + + @override + String get interval => 'Interval'; + + @override + String get endRepetition => 'End repetition'; + + @override + String get never => 'Never'; + + @override + String get onADate => 'On a date'; + + @override + String get switchDisabled => 'Switch is disabled'; + + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + + @override + String saveCsvFileFailed(Object e) { + return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + } + + @override + String errorPickingFile(Object error) { + return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + } + + @override + String get storagePermissionRequired => + 'Storage permission is required to access your files.'; + + @override + String get importingData => 'Importing data...'; + + @override + String get exportingData => 'Exporting data...'; + + @override + String fileSavedTo(Object path) { + return 'File saved to: $path'; + } + + @override + String get dataImportedSuccessfully => 'Data imported successfully'; + + @override + String get description => 'Description'; + + @override + String get addDescription => 'Add description'; + + @override + String get duplicateTransactionTitle => 'Duplicate transaction'; + + @override + String get duplicateTransactionContent => + 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + + @override + String get duplicate => 'Duplicate'; + + @override + String get moreFrequent => 'More frequent'; + + @override + String get allCategories => 'All categories'; + + @override + String get allAccounts => 'All accounts'; + + @override + String errorOccurred(Object err) { + return 'Error: $err'; + } + + @override + String get selectAccount => 'Select Account'; + + @override + String get to => 'To:'; + + @override + String get from => 'From:'; + + @override + String get recurringTransactionAdded => 'Recurring transaction added'; + + @override + String get recurringTransactions => 'Recurring transactions'; + + @override + String get addTransactionReminder => 'Add transaction reminder'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get privacyCollectTitle => 'What Information Do We Collect?'; + + @override + String get privacyChangesTitle => 'Changes to This Privacy Policy'; + + @override + String get contactUsTitle => 'Contact us'; + + @override + String get privacyIntro => + 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + + @override + String get privacyCollectBody => + 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + + @override + String get privacyChangesBody => + 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + + @override + String get contactUsBody => + 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + + @override + String get collaboratorsTitle => 'Collaborators'; + + @override + String get meetTheTeam => 'Meet the team'; + + @override + String get teamDescription => + 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + + @override + String get wantToContribute => 'Want to contribute?'; + + @override + String get contributeDescription => + 'Open an issue, submit a PR or just say hi on GitHub'; + + @override + String get appInfo => 'App Info'; + + @override + String get appVersion => 'App Version:'; + + @override + String get collaborators => 'Collaborators'; + + @override + String get collaboratorsDescription => 'See the team behind this app'; + + @override + String get privacyPolicy => 'Privacy Policy'; + + @override + String get privacyPolicyDescription => 'Read more'; + + @override + String get generalSettings => 'General Settings'; + + @override + String get appearance => 'Appearance'; + + @override + String get currency => 'Currency'; + + @override + String get requireAuthentication => 'Require authentication'; + + @override + String get searchForATransaction => 'Search for a transaction'; + + @override + String get selectACurrency => 'Select a currency'; + + @override + String get search => 'Search'; + + @override + String get searchIn => 'Search in'; + + @override + String get lastTransactions => 'Your last transactions'; + + @override + String get startReconciliation => 'Start reconciliation'; + + @override + String get newBalance => 'New balance'; + + @override + String get balanceDiscrepancy => 'Balance Discrepancy?'; + + @override + String get balanceAdjustmentHint => + 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + + @override + String get newAccount => 'New account'; + + @override + String get editAccount => 'Edit account'; + + @override + String get createAccount => 'Create account'; + + @override + String get accountName => 'Account name'; + + @override + String get name => 'Name'; + + @override + String get iconAndColor => 'Icon and color'; + + @override + String get chooseColor => 'Choose color'; + + @override + String get chooseIcon => 'Choose icon'; + + @override + String get done => 'Fatto'; + + @override + String get add => 'Add'; + + @override + String get setAsMainAccount => 'Set as main account'; + + @override + String get countsForNetWorth => 'Counts for the net worth'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get initialBalance => 'Initial balance'; + + @override + String get currentBalance => 'Current balance'; + + @override + String get showLess => 'Show less'; + + @override + String get showMore => 'Show more'; + + @override + String get addSubcategory => 'Add subcategory'; + + @override + String get newCategory => 'New category'; + + @override + String get editCategory => 'Edit category'; + + @override + String get createCategory => 'Create category'; + + @override + String get updateCategory => 'Update category'; + + @override + String get categoryName => 'Category name'; + + @override + String get type => 'Type'; + + @override + String get deleteCategory => 'Delete category'; + + @override + String get newSubcategory => 'New subcategory'; + + @override + String get editSubcategory => 'Edit subcategory'; + + @override + String get createSubcategory => 'Create subcategory'; + + @override + String get updateSubcategory => 'Update subcategory'; + + @override + String get subcategoryName => 'Subcategory name'; + + @override + String get deleteSubcategory => 'Delete subcategory'; + + @override + String get subcategory => 'Subcategory'; + + @override + String get categoryFirstThenBudget => 'Add a category first to set a budget'; + + @override + String inTheNextDays(Object next) { + return 'In $next days'; + } + + @override + String get monthlyBudget => 'Monthly budget'; + + @override + String get manage => 'Manage'; + + @override + String get swipeLeftToDelete => 'Swipe left to delete'; + + @override + String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + + @override + String get saveBudget => 'Save budget'; + + @override + String get selectCategoriesToCreateBudget => + 'Select the categories to create your budget'; + + @override + String get amount => 'Amount'; + + @override + String get addCategoryBudget => 'Add category budget'; + + @override + String get allCategoriesAdded => + 'You have already added all available categories.'; + + @override + String get delete => 'Delete'; + + @override + String get allRecurringPaymentsHere => + 'All recurring payments will be displayed here'; + + @override + String get addRecurringPayment => 'Add recurring payment'; + + @override + String get seeOlderPayments => 'See older payments'; + + @override + String untilDate(Object date) { + return 'Until $date'; + } + + @override + String get olderPayments => 'Older payments'; + + @override + String get categoryNotFound => 'Category not found'; + + @override + String get back => 'Back'; + + @override + String onTheDay(Object day) { + return '- On the $day day'; + } + + @override + String get noMonthlyPaymentHistory => 'No monthly payment history'; + + @override + String get noRecurrentPaymentHistory => 'No recurrent payment history'; + + @override + String errorLoadingPayments(Object error) { + return 'Error loading payments: $error'; + } + + @override + String get editRecurringTransaction => 'Edit recurring transaction'; + + @override + String get detailsExplanation => + 'Details (any change will affect only future transactions)'; + + @override + String get dateStart => 'Date start'; + + @override + String get planned => 'Planned'; + + @override + String get composition => 'Composition'; + + @override + String get progress => 'Progress'; + + @override + String get noBudgetSet => 'There are no budgets set'; + + @override + String get budgetHelpText => + 'A monthly budget can help you keep track of your expenses and stay within the limits'; + + @override + String get createBudget => 'Create budget'; + + @override + String get setUpTheApp => 'Set up the app'; + + @override + String get setupDescription => + 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + + @override + String get startTheSetup => 'Start the setup'; + + @override + String budgetAmount(Object amount) { + return 'Budget $amount€'; + } + + @override + String get addBudget => 'Add budget'; + + @override + String addBudgetForCategory(Object cat) { + return 'Add budget for category $cat'; + } + + @override + String get addCategory => 'Add category'; + + @override + String get confirm => 'Confirm'; + + @override + String get step1Of2 => 'Step 1 of 2'; + + @override + String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + + @override + String get chooseCategoriesForBudget => + 'Choose which categories you want to set a budget for'; + + @override + String get monthlyBudgetTotal => 'Monthly budget total:'; + + @override + String get nextStep => 'Next step'; + + @override + String get continueWithoutBudget => 'Continue without budget'; + + @override + String get step2Of2 => 'Step 2 OF 2'; + + @override + String get setLiquidityInMainAccount => + 'Set the liquidity in your main account'; + + @override + String get addMoreAccounts => + 'You\'ll be able to add more accounts within the app.'; + + @override + String get liquidityDescription => + 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + + @override + String get mainAccount => 'Main account'; + + @override + String get setAmount => 'Set amount'; + + @override + String get editIconAndColor => 'Edit icon and color'; + + @override + String get skipStepOrStartFromZero => + 'Or you can skip this step and start from 0'; + + @override + String get startTrackingExpenses => 'Start tracking your expenses'; + + @override + String get startFromZero => 'Start from 0'; + + @override + String get importExport => 'Import/Export'; + + @override + String get importData => 'Import data'; + + @override + String get importDataDescription => + 'Import a CSV file to update your database'; + + @override + String get importMoneyManager => 'Import from Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + + @override + String get exportData => 'Export data'; + + @override + String get exportDataDescription => 'Save your data as a CSV file'; + + @override + String get warningOverwrite => 'Warning: Data Overwrite'; + + @override + String get warningOverwriteContent => + 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + + @override + String get proceedImport => 'Proceed with Import'; + + @override + String get importSuccess => 'Data imported successfully'; + + @override + String exportFailed(Object err) { + return 'Export failed: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Failed to export table: $tableName'; + } + + @override + String get errorCsvNotFound => 'CSV file not found.'; + + @override + String get errorCsvEmpty => 'The CSV file is empty.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Missing expected column: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Found an unexpected value: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'A general error occurred during CSV import. With error: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Failed to import transaction on date: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Failed to clean the database. Reason: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Failed to reset the database. Reason: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transactions'; + } + + @override + String get uncategorized => 'Uncategorized'; + + @override + String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + + @override + String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + + @override + String get total => 'Total'; + + @override + String get noTransactionsAdded => 'There are no transactions added yet'; + + @override + String get addTransactionCallToAction => + 'Add a transaction to make this section more appealing'; + + @override + String get graphsEmptyState => + 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + + @override + String get availableLiquidity => 'Available liquidity'; + + @override + String get vsLastMonth => 'VS last month'; + + @override + String get monthlyBalance => 'Monthly balance'; + + @override + String get currentMonth => 'Current month'; + + @override + String get lastMonth => 'Last month'; + + @override + String get yourAccounts => 'Your accounts'; + + @override + String get yourBudgets => 'Your budgets'; + + @override + String get createBudgetToTrack => 'Create a budget to track your spending'; + + @override + String get close => 'Close'; + + @override + String get edit => 'Edit'; + + @override + String get errorDuplicatingTransaction => 'Error duplicating transaction'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" has been created'; + } + + @override + String get left => 'Left'; + + @override + String get notEnoughDataForGraph => + 'We are sorry but there is not\nenough data to make the graph...'; + + @override + String get generalSettingsDesc => 'Edit general settings'; + + @override + String get accountsDesc => 'Add or edit your accounts'; + + @override + String get categoriesDesc => 'Add/edit categories and subcategories'; + + @override + String get budget => 'Budget'; + + @override + String get budgetDesc => 'Add or edit your budgets'; + + @override + String get importExportDesc => 'Import or export data'; + + @override + String get notificationsDesc => 'Manage your notifications settings'; + + @override + String get leaveFeedback => 'Leave a feedback'; + + @override + String get leaveFeedbackDesc => + 'Complete a small form to report a bug or leave a feedback'; + + @override + String get appInfoDesc => 'Learn more about us and the app'; +} diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 001126df..2bfd4f91 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -119,6 +119,10 @@ class AppLocalizationsEn extends AppLocalizations { @override String get switchDisabled => 'Switch is disabled'; + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + @override String saveCsvFileFailed(Object e) { return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; @@ -153,10 +157,6 @@ class AppLocalizationsEn extends AppLocalizations { @override String get addDescription => 'Add description'; - @override - String get recurringTransactionWarning => - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; - @override String get duplicateTransactionTitle => 'Duplicate transaction'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart new file mode 100644 index 00000000..460786fd --- /dev/null +++ b/lib/l10n/app_localizations_es.dart @@ -0,0 +1,777 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Spanish Castilian (`es`). +class AppLocalizationsEs extends AppLocalizations { + AppLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transactions'; + + @override + String get planning => 'Planning'; + + @override + String get graphs => 'Graphs'; + + @override + String get list => 'List'; + + @override + String get categories => 'Categories'; + + @override + String get expenses => 'Expenses'; + + @override + String get incomes => 'Incomes'; + + @override + String get expense => 'Expense'; + + @override + String get income => 'Income'; + + @override + String get transfer => 'Transfer'; + + @override + String get accounts => 'Accounts'; + + @override + String get details => 'Details'; + + @override + String get account => 'Account'; + + @override + String get category => 'Category'; + + @override + String get date => 'Date'; + + @override + String get investments => 'Investments'; + + @override + String get settings => 'Settings'; + + @override + String get notifications => 'Notifications'; + + @override + String get settingsDisclaimer => 'Open source, built by the community'; + + @override + String get addTransaction => 'Add transaction'; + + @override + String get totalBalance => 'Total balance'; + + @override + String get netWorth => 'Net worth'; + + @override + String get save => 'Save'; + + @override + String get cancel => 'Cancel'; + + @override + String get success => 'Success'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Editing transaction'; + + @override + String get newTransaction => 'New transaction'; + + @override + String get updateTransaction => 'Update transaction'; + + @override + String get recurringPayments => 'Recurring payments'; + + @override + String get interval => 'Interval'; + + @override + String get endRepetition => 'End repetition'; + + @override + String get never => 'Never'; + + @override + String get onADate => 'On a date'; + + @override + String get switchDisabled => 'Switch is disabled'; + + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + + @override + String saveCsvFileFailed(Object e) { + return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + } + + @override + String errorPickingFile(Object error) { + return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + } + + @override + String get storagePermissionRequired => + 'Storage permission is required to access your files.'; + + @override + String get importingData => 'Importing data...'; + + @override + String get exportingData => 'Exporting data...'; + + @override + String fileSavedTo(Object path) { + return 'File saved to: $path'; + } + + @override + String get dataImportedSuccessfully => 'Data imported successfully'; + + @override + String get description => 'Description'; + + @override + String get addDescription => 'Add description'; + + @override + String get duplicateTransactionTitle => 'Duplicate transaction'; + + @override + String get duplicateTransactionContent => + 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + + @override + String get duplicate => 'Duplicate'; + + @override + String get moreFrequent => 'More frequent'; + + @override + String get allCategories => 'All categories'; + + @override + String get allAccounts => 'All accounts'; + + @override + String errorOccurred(Object err) { + return 'Error: $err'; + } + + @override + String get selectAccount => 'Select Account'; + + @override + String get to => 'To:'; + + @override + String get from => 'From:'; + + @override + String get recurringTransactionAdded => 'Recurring transaction added'; + + @override + String get recurringTransactions => 'Recurring transactions'; + + @override + String get addTransactionReminder => 'Add transaction reminder'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get privacyCollectTitle => 'What Information Do We Collect?'; + + @override + String get privacyChangesTitle => 'Changes to This Privacy Policy'; + + @override + String get contactUsTitle => 'Contact us'; + + @override + String get privacyIntro => + 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + + @override + String get privacyCollectBody => + 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + + @override + String get privacyChangesBody => + 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + + @override + String get contactUsBody => + 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + + @override + String get collaboratorsTitle => 'Collaborators'; + + @override + String get meetTheTeam => 'Meet the team'; + + @override + String get teamDescription => + 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + + @override + String get wantToContribute => 'Want to contribute?'; + + @override + String get contributeDescription => + 'Open an issue, submit a PR or just say hi on GitHub'; + + @override + String get appInfo => 'App Info'; + + @override + String get appVersion => 'App Version:'; + + @override + String get collaborators => 'Collaborators'; + + @override + String get collaboratorsDescription => 'See the team behind this app'; + + @override + String get privacyPolicy => 'Privacy Policy'; + + @override + String get privacyPolicyDescription => 'Read more'; + + @override + String get generalSettings => 'General Settings'; + + @override + String get appearance => 'Appearance'; + + @override + String get currency => 'Currency'; + + @override + String get requireAuthentication => 'Require authentication'; + + @override + String get searchForATransaction => 'Search for a transaction'; + + @override + String get selectACurrency => 'Select a currency'; + + @override + String get search => 'Search'; + + @override + String get searchIn => 'Search in'; + + @override + String get lastTransactions => 'Your last transactions'; + + @override + String get startReconciliation => 'Start reconciliation'; + + @override + String get newBalance => 'New balance'; + + @override + String get balanceDiscrepancy => 'Balance Discrepancy?'; + + @override + String get balanceAdjustmentHint => + 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + + @override + String get newAccount => 'New account'; + + @override + String get editAccount => 'Edit account'; + + @override + String get createAccount => 'Create account'; + + @override + String get accountName => 'Account name'; + + @override + String get name => 'Name'; + + @override + String get iconAndColor => 'Icon and color'; + + @override + String get chooseColor => 'Choose color'; + + @override + String get chooseIcon => 'Choose icon'; + + @override + String get done => 'Fatto'; + + @override + String get add => 'Add'; + + @override + String get setAsMainAccount => 'Set as main account'; + + @override + String get countsForNetWorth => 'Counts for the net worth'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get initialBalance => 'Initial balance'; + + @override + String get currentBalance => 'Current balance'; + + @override + String get showLess => 'Show less'; + + @override + String get showMore => 'Show more'; + + @override + String get addSubcategory => 'Add subcategory'; + + @override + String get newCategory => 'New category'; + + @override + String get editCategory => 'Edit category'; + + @override + String get createCategory => 'Create category'; + + @override + String get updateCategory => 'Update category'; + + @override + String get categoryName => 'Category name'; + + @override + String get type => 'Type'; + + @override + String get deleteCategory => 'Delete category'; + + @override + String get newSubcategory => 'New subcategory'; + + @override + String get editSubcategory => 'Edit subcategory'; + + @override + String get createSubcategory => 'Create subcategory'; + + @override + String get updateSubcategory => 'Update subcategory'; + + @override + String get subcategoryName => 'Subcategory name'; + + @override + String get deleteSubcategory => 'Delete subcategory'; + + @override + String get subcategory => 'Subcategory'; + + @override + String get categoryFirstThenBudget => 'Add a category first to set a budget'; + + @override + String inTheNextDays(Object next) { + return 'In $next days'; + } + + @override + String get monthlyBudget => 'Monthly budget'; + + @override + String get manage => 'Manage'; + + @override + String get swipeLeftToDelete => 'Swipe left to delete'; + + @override + String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + + @override + String get saveBudget => 'Save budget'; + + @override + String get selectCategoriesToCreateBudget => + 'Select the categories to create your budget'; + + @override + String get amount => 'Amount'; + + @override + String get addCategoryBudget => 'Add category budget'; + + @override + String get allCategoriesAdded => + 'You have already added all available categories.'; + + @override + String get delete => 'Delete'; + + @override + String get allRecurringPaymentsHere => + 'All recurring payments will be displayed here'; + + @override + String get addRecurringPayment => 'Add recurring payment'; + + @override + String get seeOlderPayments => 'See older payments'; + + @override + String untilDate(Object date) { + return 'Until $date'; + } + + @override + String get olderPayments => 'Older payments'; + + @override + String get categoryNotFound => 'Category not found'; + + @override + String get back => 'Back'; + + @override + String onTheDay(Object day) { + return '- On the $day day'; + } + + @override + String get noMonthlyPaymentHistory => 'No monthly payment history'; + + @override + String get noRecurrentPaymentHistory => 'No recurrent payment history'; + + @override + String errorLoadingPayments(Object error) { + return 'Error loading payments: $error'; + } + + @override + String get editRecurringTransaction => 'Edit recurring transaction'; + + @override + String get detailsExplanation => + 'Details (any change will affect only future transactions)'; + + @override + String get dateStart => 'Date start'; + + @override + String get planned => 'Planned'; + + @override + String get composition => 'Composition'; + + @override + String get progress => 'Progress'; + + @override + String get noBudgetSet => 'There are no budgets set'; + + @override + String get budgetHelpText => + 'A monthly budget can help you keep track of your expenses and stay within the limits'; + + @override + String get createBudget => 'Create budget'; + + @override + String get setUpTheApp => 'Set up the app'; + + @override + String get setupDescription => + 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + + @override + String get startTheSetup => 'Start the setup'; + + @override + String budgetAmount(Object amount) { + return 'Budget $amount€'; + } + + @override + String get addBudget => 'Add budget'; + + @override + String addBudgetForCategory(Object cat) { + return 'Add budget for category $cat'; + } + + @override + String get addCategory => 'Add category'; + + @override + String get confirm => 'Confirm'; + + @override + String get step1Of2 => 'Step 1 of 2'; + + @override + String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + + @override + String get chooseCategoriesForBudget => + 'Choose which categories you want to set a budget for'; + + @override + String get monthlyBudgetTotal => 'Monthly budget total:'; + + @override + String get nextStep => 'Next step'; + + @override + String get continueWithoutBudget => 'Continue without budget'; + + @override + String get step2Of2 => 'Step 2 OF 2'; + + @override + String get setLiquidityInMainAccount => + 'Set the liquidity in your main account'; + + @override + String get addMoreAccounts => + 'You\'ll be able to add more accounts within the app.'; + + @override + String get liquidityDescription => + 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + + @override + String get mainAccount => 'Main account'; + + @override + String get setAmount => 'Set amount'; + + @override + String get editIconAndColor => 'Edit icon and color'; + + @override + String get skipStepOrStartFromZero => + 'Or you can skip this step and start from 0'; + + @override + String get startTrackingExpenses => 'Start tracking your expenses'; + + @override + String get startFromZero => 'Start from 0'; + + @override + String get importExport => 'Import/Export'; + + @override + String get importData => 'Import data'; + + @override + String get importDataDescription => + 'Import a CSV file to update your database'; + + @override + String get importMoneyManager => 'Import from Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + + @override + String get exportData => 'Export data'; + + @override + String get exportDataDescription => 'Save your data as a CSV file'; + + @override + String get warningOverwrite => 'Warning: Data Overwrite'; + + @override + String get warningOverwriteContent => + 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + + @override + String get proceedImport => 'Proceed with Import'; + + @override + String get importSuccess => 'Data imported successfully'; + + @override + String exportFailed(Object err) { + return 'Export failed: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Failed to export table: $tableName'; + } + + @override + String get errorCsvNotFound => 'CSV file not found.'; + + @override + String get errorCsvEmpty => 'The CSV file is empty.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Missing expected column: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Found an unexpected value: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'A general error occurred during CSV import. With error: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Failed to import transaction on date: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Failed to clean the database. Reason: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Failed to reset the database. Reason: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transactions'; + } + + @override + String get uncategorized => 'Uncategorized'; + + @override + String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + + @override + String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + + @override + String get total => 'Total'; + + @override + String get noTransactionsAdded => 'There are no transactions added yet'; + + @override + String get addTransactionCallToAction => + 'Add a transaction to make this section more appealing'; + + @override + String get graphsEmptyState => + 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + + @override + String get availableLiquidity => 'Available liquidity'; + + @override + String get vsLastMonth => 'VS last month'; + + @override + String get monthlyBalance => 'Monthly balance'; + + @override + String get currentMonth => 'Current month'; + + @override + String get lastMonth => 'Last month'; + + @override + String get yourAccounts => 'Your accounts'; + + @override + String get yourBudgets => 'Your budgets'; + + @override + String get createBudgetToTrack => 'Create a budget to track your spending'; + + @override + String get close => 'Close'; + + @override + String get edit => 'Edit'; + + @override + String get errorDuplicatingTransaction => 'Error duplicating transaction'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" has been created'; + } + + @override + String get left => 'Left'; + + @override + String get notEnoughDataForGraph => + 'We are sorry but there is not\nenough data to make the graph...'; + + @override + String get generalSettingsDesc => 'Edit general settings'; + + @override + String get accountsDesc => 'Add or edit your accounts'; + + @override + String get categoriesDesc => 'Add/edit categories and subcategories'; + + @override + String get budget => 'Budget'; + + @override + String get budgetDesc => 'Add or edit your budgets'; + + @override + String get importExportDesc => 'Import or export data'; + + @override + String get notificationsDesc => 'Manage your notifications settings'; + + @override + String get leaveFeedback => 'Leave a feedback'; + + @override + String get leaveFeedbackDesc => + 'Complete a small form to report a bug or leave a feedback'; + + @override + String get appInfoDesc => 'Learn more about us and the app'; +} diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 03fdfa60..a38ed5c3 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -119,6 +119,10 @@ class AppLocalizationsIt extends AppLocalizations { @override String get switchDisabled => 'Gesto disabilitato'; + @override + String get recurringTransactionWarning => + 'Questa è una transazione generata da una ricorrente: qualsiasi modifica influenzerà questa transazione unica.\nPer modificare tutte le transazioni future, o le opzioni di ricorrenza, TAP QUI.'; + @override String saveCsvFileFailed(Object e) { return 'Non puoi salvare i file qui, crea o seleziona una cartella in Downloads o Documenti. Errore: \$$e'; @@ -153,10 +157,6 @@ class AppLocalizationsIt extends AppLocalizations { @override String get addDescription => 'Aggiungi descrizione'; - @override - String get recurringTransactionWarning => - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; - @override String get duplicateTransactionTitle => 'Transazione duplicata'; @@ -200,20 +200,20 @@ class AppLocalizationsIt extends AppLocalizations { String get addTransactionReminder => 'Aggiungi promemoria transazione'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Politica Sulla Privacy'; @override String get privacyCollectTitle => 'Quali informazioni raccogliamo?'; @override - String get privacyChangesTitle => 'Modifiche alla Privacy Policy'; + String get privacyChangesTitle => 'Modifiche alla politica sulla privacy'; @override String get contactUsTitle => 'Contattaci'; @override String get privacyIntro => - 'Sossoldi è sviluppata come un\'app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'; + 'Sossoldi è sviluppata come app open source. Questo servizio è fornito gratuitamente ed è inteso per essere utilizzato così com\'è.\nNon siamo interessati a raccogliere alcuna informazione personale. Riteniamo che tali informazioni siano solo tue. Non memorizziamo né trasmettiamo i tuoi dettagli personali, né includiamo software di pubblicità o analisi che comunichino con terze parti.\n'; @override String get privacyCollectBody => @@ -221,11 +221,11 @@ class AppLocalizationsIt extends AppLocalizations { @override String get privacyChangesBody => - 'Potremmo aggiornare la nostra Privacy Policy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta policy è efficace dal 01-01-2024.\n'; + 'Potremmo aggiornare la nostra politica sulla privacy di tanto in tanto. Pertanto, ti consigliamo di rivedere periodicamente questa pagina per eventuali modifiche.\nQuesta politica è efficace dal 01-01-2024.\n'; @override String get contactUsBody => - 'Se hai domande o suggerimenti sulla nostra Privacy Policy, non esitare a contattarci all\'indirizzo\n'; + 'Se hai domande o suggerimenti sulla nostra politica sulla privacy, non esitare a contattarci all\'indirizzo\n'; @override String get collaboratorsTitle => 'Collaboratori'; @@ -257,7 +257,7 @@ class AppLocalizationsIt extends AppLocalizations { String get collaboratorsDescription => 'Scopri il team dietro questa app'; @override - String get privacyPolicy => 'Privacy Policy'; + String get privacyPolicy => 'Politica Sulla Privacy'; @override String get privacyPolicyDescription => 'Leggi di più'; @@ -521,7 +521,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String budgetAmount(Object amount) { - return 'Budget $amount€'; + return 'Bilancio $amount€'; } @override @@ -566,7 +566,7 @@ class AppLocalizationsIt extends AppLocalizations { @override String get addMoreAccounts => - 'Sarai in grado di aggiungere altri account dall\'app'; + 'Sarai in grado di aggiungere altri account dall\'app.'; @override String get liquidityDescription => @@ -760,7 +760,7 @@ class AppLocalizationsIt extends AppLocalizations { String get categoriesDesc => 'Aggiungi/modifica categorie e sottocategorie'; @override - String get budget => 'Budget'; + String get budget => 'Bilancio'; @override String get budgetDesc => 'Aggiungi o modifica i tuoi budget'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart new file mode 100644 index 00000000..57d5edee --- /dev/null +++ b/lib/l10n/app_localizations_nl.dart @@ -0,0 +1,777 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Dutch Flemish (`nl`). +class AppLocalizationsNl extends AppLocalizations { + AppLocalizationsNl([String locale = 'nl']) : super(locale); + + @override + String get appName => 'Sossoldi'; + + @override + String get dashboard => 'Dashboard'; + + @override + String get transactions => 'Transactions'; + + @override + String get planning => 'Planning'; + + @override + String get graphs => 'Graphs'; + + @override + String get list => 'List'; + + @override + String get categories => 'Categories'; + + @override + String get expenses => 'Expenses'; + + @override + String get incomes => 'Incomes'; + + @override + String get expense => 'Expense'; + + @override + String get income => 'Income'; + + @override + String get transfer => 'Transfer'; + + @override + String get accounts => 'Accounts'; + + @override + String get details => 'Details'; + + @override + String get account => 'Account'; + + @override + String get category => 'Category'; + + @override + String get date => 'Date'; + + @override + String get investments => 'Investments'; + + @override + String get settings => 'Settings'; + + @override + String get notifications => 'Notifications'; + + @override + String get settingsDisclaimer => 'Open source, built by the community'; + + @override + String get addTransaction => 'Add transaction'; + + @override + String get totalBalance => 'Total balance'; + + @override + String get netWorth => 'Net worth'; + + @override + String get save => 'Save'; + + @override + String get cancel => 'Cancel'; + + @override + String get success => 'Success'; + + @override + String get ok => 'Ok'; + + @override + String get editingTransaction => 'Editing transaction'; + + @override + String get newTransaction => 'New transaction'; + + @override + String get updateTransaction => 'Update transaction'; + + @override + String get recurringPayments => 'Recurring payments'; + + @override + String get interval => 'Interval'; + + @override + String get endRepetition => 'End repetition'; + + @override + String get never => 'Never'; + + @override + String get onADate => 'On a date'; + + @override + String get switchDisabled => 'Switch is disabled'; + + @override + String get recurringTransactionWarning => + 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + + @override + String saveCsvFileFailed(Object e) { + return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + } + + @override + String errorPickingFile(Object error) { + return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + } + + @override + String get storagePermissionRequired => + 'Storage permission is required to access your files.'; + + @override + String get importingData => 'Importing data...'; + + @override + String get exportingData => 'Exporting data...'; + + @override + String fileSavedTo(Object path) { + return 'File saved to: $path'; + } + + @override + String get dataImportedSuccessfully => 'Data imported successfully'; + + @override + String get description => 'Description'; + + @override + String get addDescription => 'Add description'; + + @override + String get duplicateTransactionTitle => 'Duplicate transaction'; + + @override + String get duplicateTransactionContent => + 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + + @override + String get duplicate => 'Duplicate'; + + @override + String get moreFrequent => 'More frequent'; + + @override + String get allCategories => 'All categories'; + + @override + String get allAccounts => 'All accounts'; + + @override + String errorOccurred(Object err) { + return 'Error: $err'; + } + + @override + String get selectAccount => 'Select Account'; + + @override + String get to => 'To:'; + + @override + String get from => 'From:'; + + @override + String get recurringTransactionAdded => 'Recurring transaction added'; + + @override + String get recurringTransactions => 'Recurring transactions'; + + @override + String get addTransactionReminder => 'Add transaction reminder'; + + @override + String get privacyPolicyTitle => 'Privacy Policy'; + + @override + String get privacyCollectTitle => 'What Information Do We Collect?'; + + @override + String get privacyChangesTitle => 'Changes to This Privacy Policy'; + + @override + String get contactUsTitle => 'Contact us'; + + @override + String get privacyIntro => + 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + + @override + String get privacyCollectBody => + 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + + @override + String get privacyChangesBody => + 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + + @override + String get contactUsBody => + 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + + @override + String get collaboratorsTitle => 'Collaborators'; + + @override + String get meetTheTeam => 'Meet the team'; + + @override + String get teamDescription => + 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + + @override + String get wantToContribute => 'Want to contribute?'; + + @override + String get contributeDescription => + 'Open an issue, submit a PR or just say hi on GitHub'; + + @override + String get appInfo => 'App Info'; + + @override + String get appVersion => 'App Version:'; + + @override + String get collaborators => 'Collaborators'; + + @override + String get collaboratorsDescription => 'See the team behind this app'; + + @override + String get privacyPolicy => 'Privacy Policy'; + + @override + String get privacyPolicyDescription => 'Read more'; + + @override + String get generalSettings => 'General Settings'; + + @override + String get appearance => 'Appearance'; + + @override + String get currency => 'Currency'; + + @override + String get requireAuthentication => 'Require authentication'; + + @override + String get searchForATransaction => 'Search for a transaction'; + + @override + String get selectACurrency => 'Select a currency'; + + @override + String get search => 'Search'; + + @override + String get searchIn => 'Search in'; + + @override + String get lastTransactions => 'Your last transactions'; + + @override + String get startReconciliation => 'Start reconciliation'; + + @override + String get newBalance => 'New balance'; + + @override + String get balanceDiscrepancy => 'Balance Discrepancy?'; + + @override + String get balanceAdjustmentHint => + 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + + @override + String get newAccount => 'New account'; + + @override + String get editAccount => 'Edit account'; + + @override + String get createAccount => 'Create account'; + + @override + String get accountName => 'Account name'; + + @override + String get name => 'Name'; + + @override + String get iconAndColor => 'Icon and color'; + + @override + String get chooseColor => 'Choose color'; + + @override + String get chooseIcon => 'Choose icon'; + + @override + String get done => 'Fatto'; + + @override + String get add => 'Add'; + + @override + String get setAsMainAccount => 'Set as main account'; + + @override + String get countsForNetWorth => 'Counts for the net worth'; + + @override + String get deleteAccount => 'Delete account'; + + @override + String get initialBalance => 'Initial balance'; + + @override + String get currentBalance => 'Current balance'; + + @override + String get showLess => 'Show less'; + + @override + String get showMore => 'Show more'; + + @override + String get addSubcategory => 'Add subcategory'; + + @override + String get newCategory => 'New category'; + + @override + String get editCategory => 'Edit category'; + + @override + String get createCategory => 'Create category'; + + @override + String get updateCategory => 'Update category'; + + @override + String get categoryName => 'Category name'; + + @override + String get type => 'Type'; + + @override + String get deleteCategory => 'Delete category'; + + @override + String get newSubcategory => 'New subcategory'; + + @override + String get editSubcategory => 'Edit subcategory'; + + @override + String get createSubcategory => 'Create subcategory'; + + @override + String get updateSubcategory => 'Update subcategory'; + + @override + String get subcategoryName => 'Subcategory name'; + + @override + String get deleteSubcategory => 'Delete subcategory'; + + @override + String get subcategory => 'Subcategory'; + + @override + String get categoryFirstThenBudget => 'Add a category first to set a budget'; + + @override + String inTheNextDays(Object next) { + return 'In $next days'; + } + + @override + String get monthlyBudget => 'Monthly budget'; + + @override + String get manage => 'Manage'; + + @override + String get swipeLeftToDelete => 'Swipe left to delete'; + + @override + String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + + @override + String get saveBudget => 'Save budget'; + + @override + String get selectCategoriesToCreateBudget => + 'Select the categories to create your budget'; + + @override + String get amount => 'Amount'; + + @override + String get addCategoryBudget => 'Add category budget'; + + @override + String get allCategoriesAdded => + 'You have already added all available categories.'; + + @override + String get delete => 'Delete'; + + @override + String get allRecurringPaymentsHere => + 'All recurring payments will be displayed here'; + + @override + String get addRecurringPayment => 'Add recurring payment'; + + @override + String get seeOlderPayments => 'See older payments'; + + @override + String untilDate(Object date) { + return 'Until $date'; + } + + @override + String get olderPayments => 'Older payments'; + + @override + String get categoryNotFound => 'Category not found'; + + @override + String get back => 'Back'; + + @override + String onTheDay(Object day) { + return '- On the $day day'; + } + + @override + String get noMonthlyPaymentHistory => 'No monthly payment history'; + + @override + String get noRecurrentPaymentHistory => 'No recurrent payment history'; + + @override + String errorLoadingPayments(Object error) { + return 'Error loading payments: $error'; + } + + @override + String get editRecurringTransaction => 'Edit recurring transaction'; + + @override + String get detailsExplanation => + 'Details (any change will affect only future transactions)'; + + @override + String get dateStart => 'Date start'; + + @override + String get planned => 'Planned'; + + @override + String get composition => 'Composition'; + + @override + String get progress => 'Progress'; + + @override + String get noBudgetSet => 'There are no budgets set'; + + @override + String get budgetHelpText => + 'A monthly budget can help you keep track of your expenses and stay within the limits'; + + @override + String get createBudget => 'Create budget'; + + @override + String get setUpTheApp => 'Set up the app'; + + @override + String get setupDescription => + 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + + @override + String get startTheSetup => 'Start the setup'; + + @override + String budgetAmount(Object amount) { + return 'Budget $amount€'; + } + + @override + String get addBudget => 'Add budget'; + + @override + String addBudgetForCategory(Object cat) { + return 'Add budget for category $cat'; + } + + @override + String get addCategory => 'Add category'; + + @override + String get confirm => 'Confirm'; + + @override + String get step1Of2 => 'Step 1 of 2'; + + @override + String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + + @override + String get chooseCategoriesForBudget => + 'Choose which categories you want to set a budget for'; + + @override + String get monthlyBudgetTotal => 'Monthly budget total:'; + + @override + String get nextStep => 'Next step'; + + @override + String get continueWithoutBudget => 'Continue without budget'; + + @override + String get step2Of2 => 'Step 2 OF 2'; + + @override + String get setLiquidityInMainAccount => + 'Set the liquidity in your main account'; + + @override + String get addMoreAccounts => + 'You\'ll be able to add more accounts within the app.'; + + @override + String get liquidityDescription => + 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + + @override + String get mainAccount => 'Main account'; + + @override + String get setAmount => 'Set amount'; + + @override + String get editIconAndColor => 'Edit icon and color'; + + @override + String get skipStepOrStartFromZero => + 'Or you can skip this step and start from 0'; + + @override + String get startTrackingExpenses => 'Start tracking your expenses'; + + @override + String get startFromZero => 'Start from 0'; + + @override + String get importExport => 'Import/Export'; + + @override + String get importData => 'Import data'; + + @override + String get importDataDescription => + 'Import a CSV file to update your database'; + + @override + String get importMoneyManager => 'Import from Money Manager'; + + @override + String get importMoneyManagerDescription => + 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + + @override + String get exportData => 'Export data'; + + @override + String get exportDataDescription => 'Save your data as a CSV file'; + + @override + String get warningOverwrite => 'Warning: Data Overwrite'; + + @override + String get warningOverwriteContent => + 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + + @override + String get proceedImport => 'Proceed with Import'; + + @override + String get importSuccess => 'Data imported successfully'; + + @override + String exportFailed(Object err) { + return 'Export failed: $err'; + } + + @override + String errorExporting(Object tableName) { + return 'Failed to export table: $tableName'; + } + + @override + String get errorCsvNotFound => 'CSV file not found.'; + + @override + String get errorCsvEmpty => 'The CSV file is empty.'; + + @override + String errorCsvExpectedColumn(Object column) { + return 'Missing expected column: $column'; + } + + @override + String errorCsvUnexpectedValue(Object value) { + return 'Found an unexpected value: $value'; + } + + @override + String errorCsvImportGeneral(Object error) { + return 'A general error occurred during CSV import. With error: $error'; + } + + @override + String errorCsvTransactionImport(Object date) { + return 'Failed to import transaction on date: $date'; + } + + @override + String errorCleanDatabase(Object error) { + return 'Failed to clean the database. Reason: $error'; + } + + @override + String errorResetDatabase(Object error) { + return 'Failed to reset the database. Reason: $error'; + } + + @override + String transactionCount(Object count) { + return '$count transactions'; + } + + @override + String get uncategorized => 'Uncategorized'; + + @override + String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + + @override + String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + + @override + String get total => 'Total'; + + @override + String get noTransactionsAdded => 'There are no transactions added yet'; + + @override + String get addTransactionCallToAction => + 'Add a transaction to make this section more appealing'; + + @override + String get graphsEmptyState => + 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + + @override + String get availableLiquidity => 'Available liquidity'; + + @override + String get vsLastMonth => 'VS last month'; + + @override + String get monthlyBalance => 'Monthly balance'; + + @override + String get currentMonth => 'Current month'; + + @override + String get lastMonth => 'Last month'; + + @override + String get yourAccounts => 'Your accounts'; + + @override + String get yourBudgets => 'Your budgets'; + + @override + String get createBudgetToTrack => 'Create a budget to track your spending'; + + @override + String get close => 'Close'; + + @override + String get edit => 'Edit'; + + @override + String get errorDuplicatingTransaction => 'Error duplicating transaction'; + + @override + String transactionCreated(Object transaction) { + return '\"$transaction\" has been created'; + } + + @override + String get left => 'Left'; + + @override + String get notEnoughDataForGraph => + 'We are sorry but there is not\nenough data to make the graph...'; + + @override + String get generalSettingsDesc => 'Edit general settings'; + + @override + String get accountsDesc => 'Add or edit your accounts'; + + @override + String get categoriesDesc => 'Add/edit categories and subcategories'; + + @override + String get budget => 'Budget'; + + @override + String get budgetDesc => 'Add or edit your budgets'; + + @override + String get importExportDesc => 'Import or export data'; + + @override + String get notificationsDesc => 'Manage your notifications settings'; + + @override + String get leaveFeedback => 'Leave a feedback'; + + @override + String get leaveFeedbackDesc => + 'Complete a small form to report a bug or leave a feedback'; + + @override + String get appInfoDesc => 'Learn more about us and the app'; +} diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index ceef5941..a9bdc5f0 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -12,7 +12,7 @@ class AppLocalizationsPt extends AppLocalizations { String get appName => 'Sossoldi'; @override - String get dashboard => 'Dashboard'; + String get dashboard => 'Painel'; @override String get transactions => 'Transações'; @@ -119,6 +119,10 @@ class AppLocalizationsPt extends AppLocalizations { @override String get switchDisabled => 'Gesto desativado'; + @override + String get recurringTransactionWarning => + 'Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.'; + @override String saveCsvFileFailed(Object e) { return 'Não é possível salvar arquivos aqui, crie ou selecione uma pasta em Downloads ou Documentos. Erro: \$$e'; @@ -153,10 +157,6 @@ class AppLocalizationsPt extends AppLocalizations { @override String get addDescription => 'Adicionar descrição'; - @override - String get recurringTransactionWarning => - 'Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.'; - @override String get duplicateTransactionTitle => 'Duplicar transação'; diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb new file mode 100644 index 00000000..91a663f6 --- /dev/null +++ b/lib/l10n/app_nl.arb @@ -0,0 +1,239 @@ +{ + "@@locale": "nl", + "appName": "Sossoldi", + "@appName": { + "description": "The name of the application" + }, + "dashboard": "Dashboard", + "transactions": "Transactions", + "planning": "Planning", + "graphs": "Graphs", + "list": "List", + "categories": "Categories", + "expenses": "Expenses", + "incomes": "Incomes", + "expense": "Expense", + "income": "Income", + "transfer": "Transfer", + "accounts": "Accounts", + "details": "Details", + "account": "Account", + "category": "Category", + "date": "Date", + "investments": "Investments", + "settings": "Settings", + "notifications": "Notifications", + "settingsDisclaimer": "Open source, built by the community", + "addTransaction": "Add transaction", + "totalBalance": "Total balance", + "netWorth": "Net worth", + "save": "Save", + "cancel": "Cancel", + "success": "Success", + "ok": "Ok", + "editingTransaction": "Editing transaction", + "newTransaction": "New transaction", + "updateTransaction": "Update transaction", + "recurringPayments": "Recurring payments", + "interval": "Interval", + "endRepetition": "End repetition", + "never": "Never", + "onADate": "On a date", + "switchDisabled": "Switch is disabled", + "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", + "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", + "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", + "storagePermissionRequired": "Storage permission is required to access your files.", + "importingData": "Importing data...", + "exportingData": "Exporting data...", + "fileSavedTo": "File saved to: {path}", + "dataImportedSuccessfully": "Data imported successfully", + "description": "Description", + "addDescription": "Add description", + "duplicateTransactionTitle": "Duplicate transaction", + "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", + "duplicate": "Duplicate", + "moreFrequent": "More frequent", + "allCategories": "All categories", + "allAccounts": "All accounts", + "errorOccurred": "Error: {err}", + "selectAccount": "Select Account", + "to": "To:", + "from": "From:", + "recurringTransactionAdded": "Recurring transaction added", + "recurringTransactions": "Recurring transactions", + "addTransactionReminder": "Add transaction reminder", + "privacyPolicyTitle": "Privacy Policy", + "privacyCollectTitle": "What Information Do We Collect?", + "privacyChangesTitle": "Changes to This Privacy Policy", + "contactUsTitle": "Contact us", + "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", + "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", + "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", + "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", + "collaboratorsTitle": "Collaborators", + "meetTheTeam": "Meet the team", + "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", + "wantToContribute": "Want to contribute?", + "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", + "appInfo": "App Info", + "appVersion": "App Version:", + "collaborators": "Collaborators", + "collaboratorsDescription": "See the team behind this app", + "privacyPolicy": "Privacy Policy", + "privacyPolicyDescription": "Read more", + "generalSettings": "General Settings", + "appearance": "Appearance", + "currency": "Currency", + "requireAuthentication": "Require authentication", + "searchForATransaction": "Search for a transaction", + "selectACurrency": "Select a currency", + "search": "Search", + "searchIn": "Search in", + "lastTransactions": "Your last transactions", + "startReconciliation": "Start reconciliation", + "newBalance": "New balance", + "balanceDiscrepancy": "Balance Discrepancy?", + "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", + "newAccount": "New account", + "editAccount": "Edit account", + "createAccount": "Create account", + "accountName": "Account name", + "name": "Name", + "iconAndColor": "Icon and color", + "chooseColor": "Choose color", + "chooseIcon": "Choose icon", + "done": "Fatto", + "add": "Add", + "setAsMainAccount": "Set as main account", + "countsForNetWorth": "Counts for the net worth", + "deleteAccount": "Delete account", + "initialBalance": "Initial balance", + "currentBalance": "Current balance", + "showLess": "Show less", + "showMore": "Show more", + "addSubcategory": "Add subcategory", + "newCategory": "New category", + "editCategory": "Edit category", + "createCategory": "Create category", + "updateCategory": "Update category", + "categoryName": "Category name", + "type": "Type", + "deleteCategory": "Delete category", + "newSubcategory": "New subcategory", + "editSubcategory": "Edit subcategory", + "createSubcategory": "Create subcategory", + "updateSubcategory": "Update subcategory", + "subcategoryName": "Subcategory name", + "deleteSubcategory": "Delete subcategory", + "subcategory": "Subcategory", + "categoryFirstThenBudget": "Add a category first to set a budget", + "inTheNextDays": "In {next} days", + "monthlyBudget": "Monthly budget", + "manage": "Manage", + "swipeLeftToDelete": "Swipe left to delete", + "yourMonthlyBudgetWillBe": "Your monthly budget will be:", + "saveBudget": "Save budget", + "selectCategoriesToCreateBudget": "Select the categories to create your budget", + "amount": "Amount", + "addCategoryBudget": "Add category budget", + "allCategoriesAdded": "You have already added all available categories.", + "delete": "Delete", + "allRecurringPaymentsHere": "All recurring payments will be displayed here", + "addRecurringPayment": "Add recurring payment", + "seeOlderPayments": "See older payments", + "untilDate": "Until {date}", + "olderPayments": "Older payments", + "categoryNotFound": "Category not found", + "back": "Back", + "onTheDay": "- On the {day} day", + "noMonthlyPaymentHistory": "No monthly payment history", + "noRecurrentPaymentHistory": "No recurrent payment history", + "errorLoadingPayments": "Error loading payments: {error}", + "editRecurringTransaction": "Edit recurring transaction", + "detailsExplanation": "Details (any change will affect only future transactions)", + "dateStart": "Date start", + "planned": "Planned", + "composition": "Composition", + "progress": "Progress", + "noBudgetSet": "There are no budgets set", + "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", + "createBudget": "Create budget", + "setUpTheApp": "Set up the app", + "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", + "startTheSetup": "Start the setup", + "budgetAmount": "Budget {amount}€", + "addBudget": "Add budget", + "addBudgetForCategory": "Add budget for category {cat}", + "addCategory": "Add category", + "confirm": "Confirm", + "step1Of2": "Step 1 of 2", + "setupMonthlyBudgets": "Set up your monthly\nbudgets", + "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", + "monthlyBudgetTotal": "Monthly budget total:", + "nextStep": "Next step", + "continueWithoutBudget": "Continue without budget", + "step2Of2": "Step 2 OF 2", + "setLiquidityInMainAccount": "Set the liquidity in your main account", + "addMoreAccounts": "You'll be able to add more accounts within the app.", + "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", + "mainAccount": "Main account", + "setAmount": "Set amount", + "editIconAndColor": "Edit icon and color", + "skipStepOrStartFromZero": "Or you can skip this step and start from 0", + "startTrackingExpenses": "Start tracking your expenses", + "startFromZero": "Start from 0", + "importExport": "Import/Export", + "importData": "Import data", + "importDataDescription": "Import a CSV file to update your database", + "importMoneyManager": "Import from Money Manager", + "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", + "exportData": "Export data", + "exportDataDescription": "Save your data as a CSV file", + "warningOverwrite": "Warning: Data Overwrite", + "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", + "proceedImport": "Proceed with Import", + "importSuccess": "Data imported successfully", + "exportFailed": "Export failed: {err}", + "errorExporting": "Failed to export table: {tableName}", + "errorCsvNotFound": "CSV file not found.", + "errorCsvEmpty": "The CSV file is empty.", + "errorCsvExpectedColumn": "Missing expected column: {column}", + "errorCsvUnexpectedValue": "Found an unexpected value: {value}", + "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", + "errorCsvTransactionImport": "Failed to import transaction on date: {date}", + "errorCleanDatabase": "Failed to clean the database. Reason: {error}", + "errorResetDatabase": "Failed to reset the database. Reason: {error}", + "transactionCount": "{count} transactions", + "uncategorized": "Uncategorized", + "noIncomesForSelectedMonth": "No incomes for the selected month", + "noExpensesForSelectedMonth": "No expenses for the selected month", + "total": "Total", + "noTransactionsAdded": "There are no transactions added yet", + "addTransactionCallToAction": "Add a transaction to make this section more appealing", + "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", + "availableLiquidity": "Available liquidity", + "vsLastMonth": "VS last month", + "monthlyBalance": "Monthly balance", + "currentMonth": "Current month", + "lastMonth": "Last month", + "yourAccounts": "Your accounts", + "yourBudgets": "Your budgets", + "createBudgetToTrack": "Create a budget to track your spending", + "close": "Close", + "edit": "Edit", + "errorDuplicatingTransaction": "Error duplicating transaction", + "transactionCreated": "\"{transaction}\" has been created", + "left": "Left", + "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", + "generalSettingsDesc": "Edit general settings", + "accountsDesc": "Add or edit your accounts", + "categoriesDesc": "Add/edit categories and subcategories", + "budget": "Budget", + "budgetDesc": "Add or edit your budgets", + "importExportDesc": "Import or export data", + "notificationsDesc": "Manage your notifications settings", + "leaveFeedback": "Leave a feedback", + "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", + "appInfoDesc": "Learn more about us and the app" +} \ No newline at end of file diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 9e6fcc33..98629195 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -2,9 +2,9 @@ "@@locale": "pt", "appName": "Sossoldi", "@appName": { - "description": "O nome da aplicação" + "description": "The name of the application" }, - "dashboard": "Dashboard", + "dashboard": "Painel", "transactions": "Transações", "planning": "Planejamento", "graphs": "Gráficos", @@ -40,6 +40,7 @@ "never": "Nunca", "onADate": "Em uma data", "switchDisabled": "Gesto desativado", + "recurringTransactionWarning": "Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.", "saveCsvFileFailed": "Não é possível salvar arquivos aqui, crie ou selecione uma pasta em Downloads ou Documentos. Erro: ${e}", "errorPickingFile": "Erro ao selecionar o arquivo. Certifique-se de ter as permissões necessárias. Erro: {error}", "storagePermissionRequired": "É necessária permissão de armazenamento para acessar os arquivos.", @@ -49,7 +50,6 @@ "dataImportedSuccessfully": "Dados importados com sucesso", "description": "Descrição", "addDescription": "Adicionar descrição", - "recurringTransactionWarning": "Esta é uma transação gerada por uma recorrente: qualquer alteração afetará apenas esta transação.\nPara alterar todas as transações futuras ou opções de recorrência, TOQUE AQUI.", "duplicateTransactionTitle": "Duplicar transação", "duplicateTransactionContent": "Esta transação já está na lista. Deseja duplicá-la? Você poderá editar a nova entrada posteriormente.", "duplicate": "Duplicar", @@ -226,7 +226,6 @@ "transactionCreated": "\"{transaction}\" foi criada", "left": "Restante", "notEnoughDataForGraph": "Lamentamos, mas não há\n-dados suficientes para criar o gráfico...", - "generalSettingsDesc": "Editar configurações gerais", "accountsDesc": "Adicionar ou editar suas contas", "categoriesDesc": "Adicionar/editar categorias e subcategorias", diff --git a/lib/main.dart b/lib/main.dart index efd0e198..a0809f7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -110,7 +110,9 @@ class Launcher extends ConsumerWidget { supportedLocales: [ const Locale('en'), // English const Locale('pt'), // Portuguese - // const Locale('es'), // Spanish + const Locale('es'), // Spanish + const Locale('de'), // German + const Locale('nl'), // Dutch const Locale('it'), // Italian // Locale('zh'), // Chinese // Locale('hi'), // Hindi From 02c2339825b0d1ae21f9fa665a089a95cd55d275 Mon Sep 17 00:00:00 2001 From: Mattia-Sacchi <106739902+Mattia-Sacchi@users.noreply.github.com> Date: Wed, 11 Mar 2026 22:29:10 +0100 Subject: [PATCH 20/20] Translation for de, es, nl (this ones are AI generated not to trust) --- lib/l10n/app_de.arb | 452 ++++++++++++++-------------- lib/l10n/app_es.arb | 458 ++++++++++++++-------------- lib/l10n/app_localizations_de.dart | 461 ++++++++++++++-------------- lib/l10n/app_localizations_es.dart | 464 +++++++++++++++-------------- lib/l10n/app_localizations_nl.dart | 454 ++++++++++++++-------------- lib/l10n/app_nl.arb | 446 +++++++++++++-------------- lib/main.dart | 6 +- 7 files changed, 1372 insertions(+), 1369 deletions(-) diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index b1f63d26..a1979ff5 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -2,238 +2,238 @@ "@@locale": "de", "appName": "Sossoldi", "@appName": { - "description": "The name of the application" + "description": "Der Name der Anwendung" }, "dashboard": "Dashboard", - "transactions": "Transactions", - "planning": "Planning", - "graphs": "Graphs", - "list": "List", - "categories": "Categories", - "expenses": "Expenses", - "incomes": "Incomes", - "expense": "Expense", - "income": "Income", - "transfer": "Transfer", - "accounts": "Accounts", + "transactions": "Transaktionen", + "planning": "Planung", + "graphs": "Grafiken", + "list": "Liste", + "categories": "Kategorien", + "expenses": "Ausgaben", + "incomes": "Einnahmen", + "expense": "Ausgabe", + "income": "Einnahme", + "transfer": "Überweisung", + "accounts": "Konten", "details": "Details", - "account": "Account", - "category": "Category", - "date": "Date", - "investments": "Investments", - "settings": "Settings", - "notifications": "Notifications", - "settingsDisclaimer": "Open source, built by the community", - "addTransaction": "Add transaction", - "totalBalance": "Total balance", - "netWorth": "Net worth", - "save": "Save", - "cancel": "Cancel", - "success": "Success", + "account": "Konto", + "category": "Kategorie", + "date": "Datum", + "investments": "Investitionen", + "settings": "Einstellungen", + "notifications": "Benachrichtigungen", + "settingsDisclaimer": "Open Source, von der Community entwickelt", + "addTransaction": "Transaktion hinzufügen", + "totalBalance": "Gesamtsaldo", + "netWorth": "Nettovermögen", + "save": "Speichern", + "cancel": "Abbrechen", + "success": "Erfolg", "ok": "Ok", - "editingTransaction": "Editing transaction", - "newTransaction": "New transaction", - "updateTransaction": "Update transaction", - "recurringPayments": "Recurring payments", - "interval": "Interval", - "endRepetition": "End repetition", - "never": "Never", - "onADate": "On a date", - "switchDisabled": "Switch is disabled", - "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", - "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", - "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", - "storagePermissionRequired": "Storage permission is required to access your files.", - "importingData": "Importing data...", - "exportingData": "Exporting data...", - "fileSavedTo": "File saved to: {path}", - "dataImportedSuccessfully": "Data imported successfully", - "description": "Description", - "addDescription": "Add description", - "duplicateTransactionTitle": "Duplicate transaction", - "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", - "duplicate": "Duplicate", - "moreFrequent": "More frequent", - "allCategories": "All categories", - "allAccounts": "All accounts", - "errorOccurred": "Error: {err}", - "selectAccount": "Select Account", - "to": "To:", - "from": "From:", - "recurringTransactionAdded": "Recurring transaction added", - "recurringTransactions": "Recurring transactions", - "addTransactionReminder": "Add transaction reminder", - "privacyPolicyTitle": "Privacy Policy", - "privacyCollectTitle": "What Information Do We Collect?", - "privacyChangesTitle": "Changes to This Privacy Policy", - "contactUsTitle": "Contact us", - "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", - "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", - "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", - "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", - "collaboratorsTitle": "Collaborators", - "meetTheTeam": "Meet the team", - "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", - "wantToContribute": "Want to contribute?", - "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", - "appInfo": "App Info", - "appVersion": "App Version:", - "collaborators": "Collaborators", - "collaboratorsDescription": "See the team behind this app", - "privacyPolicy": "Privacy Policy", - "privacyPolicyDescription": "Read more", - "generalSettings": "General Settings", - "appearance": "Appearance", - "currency": "Currency", - "requireAuthentication": "Require authentication", - "searchForATransaction": "Search for a transaction", - "selectACurrency": "Select a currency", - "search": "Search", - "searchIn": "Search in", - "lastTransactions": "Your last transactions", - "startReconciliation": "Start reconciliation", - "newBalance": "New balance", - "balanceDiscrepancy": "Balance Discrepancy?", - "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", - "newAccount": "New account", - "editAccount": "Edit account", - "createAccount": "Create account", - "accountName": "Account name", + "editingTransaction": "Transaktion bearbeiten", + "newTransaction": "Neue Transaktion", + "updateTransaction": "Transaktion aktualisieren", + "recurringPayments": "Wiederkehrende Zahlungen", + "interval": "Intervall", + "endRepetition": "Wiederholung beenden", + "never": "Nie", + "onADate": "An einem Datum", + "switchDisabled": "Schalter ist deaktiviert", + "recurringTransactionWarning": "Dies ist eine von einer wiederkehrenden Transaktion generierte Buchung: Jede Änderung betrifft nur diese einzelne Transaktion.\nUm alle zukünftigen Transaktionen oder Wiederholungsoptionen zu ändern, HIER TIPPEN.", + "saveCsvFileFailed": "Datei kann hier nicht gespeichert werden. Bitte wählen Sie einen Ordner in Downloads oder Dokumente. Fehler: {e}", + "errorPickingFile": "Fehler beim Auswählen der Datei. Bitte stellen Sie sicher, dass Sie über ausreichende Berechtigungen verfügen. Fehler: {error}", + "storagePermissionRequired": "Speicherberechtigung ist erforderlich, um auf Ihre Dateien zuzugreifen.", + "importingData": "Daten werden importiert...", + "exportingData": "Daten werden exportiert...", + "fileSavedTo": "Datei gespeichert unter: {path}", + "dataImportedSuccessfully": "Daten erfolgreich importiert", + "description": "Beschreibung", + "addDescription": "Beschreibung hinzufügen", + "duplicateTransactionTitle": "Transaktion duplizieren", + "duplicateTransactionContent": "Diese Transaktion ist bereits in der Liste. Möchten Sie sie duplizieren? Sie können die neue Transaktion anschließend bearbeiten.", + "duplicate": "Duplizieren", + "moreFrequent": "Häufiger", + "allCategories": "Alle Kategorien", + "allAccounts": "Alle Konten", + "errorOccurred": "Fehler: {err}", + "selectAccount": "Konto auswählen", + "to": "An:", + "from": "Von:", + "recurringTransactionAdded": "Wiederkehrende Transaktion hinzugefügt", + "recurringTransactions": "Wiederkehrende Transaktionen", + "addTransactionReminder": "Transaktionserinnerung hinzufügen", + "privacyPolicyTitle": "Datenschutzerklärung", + "privacyCollectTitle": "Welche Informationen sammeln wir?", + "privacyChangesTitle": "Änderungen an dieser Datenschutzerklärung", + "contactUsTitle": "Kontaktieren Sie uns", + "privacyIntro": "Sossoldi ist als Open-Source-App konzipiert. Dieser Dienst wird von uns kostenlos zur Verfügung gestellt und ist für die Nutzung im Ist-Zustand bestimmt.\nWir sind nicht daran interessiert, persönliche Informationen zu sammeln. Wir glauben, dass diese Informationen ausschließlich Ihnen gehören. Wir speichern oder übertragen Ihre persönlichen Daten nicht und binden keine Werbe- oder Analyse-Software ein, die mit Dritten kommuniziert.\n", + "privacyCollectBody": "Sossoldi sammelt keine persönlichen Daten und verbindet sich nicht mit dem Internet. Alle Informationen, die Sie in der App hinzufügen, existieren ausschließlich auf Ihrem Gerät und nirgendwo sonst.\n", + "privacyChangesBody": "Wir können unsere Datenschutzerklärung von Zeit zu Zeit aktualisieren. Daher wird empfohlen, diese Seite regelmäßig auf Änderungen zu überprüfen.\nDiese Richtlinie ist gültig ab 2024-01-01.\n", + "contactUsBody": "Wenn Sie Fragen oder Anregungen zu unserer Datenschutzerklärung haben, zögern Sie nicht, uns zu kontaktieren unter \n", + "collaboratorsTitle": "Mitwirkende", + "meetTheTeam": "Das Team", + "teamDescription": "Sossoldi wird von einer leidenschaftlichen Open-Source-Community entwickelt und gepflegt. Jede Funktion, jeder Fix und jede Idee kommt von Menschen wie Ihnen.", + "wantToContribute": "Möchten Sie mitwirken?", + "contributeDescription": "Öffnen Sie ein Issue, senden Sie einen PR oder sagen Sie einfach Hallo auf GitHub", + "appInfo": "App-Info", + "appVersion": "App-Version:", + "collaborators": "Mitwirkende", + "collaboratorsDescription": "Sehen Sie sich das Team hinter dieser App an", + "privacyPolicy": "Datenschutzerklärung", + "privacyPolicyDescription": "Mehr lesen", + "generalSettings": "Allgemeine Einstellungen", + "appearance": "Erscheinungsbild", + "currency": "Währung", + "requireAuthentication": "Authentifizierung anfordern", + "searchForATransaction": "Nach einer Transaktion suchen", + "selectACurrency": "Währung auswählen", + "search": "Suche", + "searchIn": "Suchen in", + "lastTransactions": "Ihre letzten Transaktionen", + "startReconciliation": "Abgleich starten", + "newBalance": "Neuer Kontostand", + "balanceDiscrepancy": "Saldo-Diskrepanz?", + "balanceAdjustmentHint": "Ihr erfasster Saldo kann von Ihrem Bankbeleg abweichen. Tippen Sie unten, um Ihren Saldo manuell anzupassen.", + "newAccount": "Neues Konto", + "editAccount": "Konto bearbeiten", + "createAccount": "Konto erstellen", + "accountName": "Kontoname", "name": "Name", - "iconAndColor": "Icon and color", - "chooseColor": "Choose color", - "chooseIcon": "Choose icon", - "done": "Fatto", - "add": "Add", - "setAsMainAccount": "Set as main account", - "countsForNetWorth": "Counts for the net worth", - "deleteAccount": "Delete account", - "initialBalance": "Initial balance", - "currentBalance": "Current balance", - "showLess": "Show less", - "showMore": "Show more", - "addSubcategory": "Add subcategory", - "newCategory": "New category", - "editCategory": "Edit category", - "createCategory": "Create category", - "updateCategory": "Update category", - "categoryName": "Category name", - "type": "Type", - "deleteCategory": "Delete category", - "newSubcategory": "New subcategory", - "editSubcategory": "Edit subcategory", - "createSubcategory": "Create subcategory", - "updateSubcategory": "Update subcategory", - "subcategoryName": "Subcategory name", - "deleteSubcategory": "Delete subcategory", - "subcategory": "Subcategory", - "categoryFirstThenBudget": "Add a category first to set a budget", - "inTheNextDays": "In {next} days", - "monthlyBudget": "Monthly budget", - "manage": "Manage", - "swipeLeftToDelete": "Swipe left to delete", - "yourMonthlyBudgetWillBe": "Your monthly budget will be:", - "saveBudget": "Save budget", - "selectCategoriesToCreateBudget": "Select the categories to create your budget", - "amount": "Amount", - "addCategoryBudget": "Add category budget", - "allCategoriesAdded": "You have already added all available categories.", - "delete": "Delete", - "allRecurringPaymentsHere": "All recurring payments will be displayed here", - "addRecurringPayment": "Add recurring payment", - "seeOlderPayments": "See older payments", - "untilDate": "Until {date}", - "olderPayments": "Older payments", - "categoryNotFound": "Category not found", - "back": "Back", - "onTheDay": "- On the {day} day", - "noMonthlyPaymentHistory": "No monthly payment history", - "noRecurrentPaymentHistory": "No recurrent payment history", - "errorLoadingPayments": "Error loading payments: {error}", - "editRecurringTransaction": "Edit recurring transaction", - "detailsExplanation": "Details (any change will affect only future transactions)", - "dateStart": "Date start", - "planned": "Planned", - "composition": "Composition", - "progress": "Progress", - "noBudgetSet": "There are no budgets set", - "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", - "createBudget": "Create budget", - "setUpTheApp": "Set up the app", - "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", - "startTheSetup": "Start the setup", + "iconAndColor": "Symbol und Farbe", + "chooseColor": "Farbe wählen", + "chooseIcon": "Symbol wählen", + "done": "Fertig", + "add": "Hinzufügen", + "setAsMainAccount": "Als Hauptkonto festlegen", + "countsForNetWorth": "Zählt für das Nettovermögen", + "deleteAccount": "Konto löschen", + "initialBalance": "Anfangssaldo", + "currentBalance": "Aktueller Saldo", + "showLess": "Weniger anzeigen", + "showMore": "Mehr anzeigen", + "addSubcategory": "Unterkategorie hinzufügen", + "newCategory": "Neue Kategorie", + "editCategory": "Kategorie bearbeiten", + "createCategory": "Kategorie erstellen", + "updateCategory": "Kategorie aktualisieren", + "categoryName": "Kategoriename", + "type": "Typ", + "deleteCategory": "Kategorie löschen", + "newSubcategory": "Neue Unterkategorie", + "editSubcategory": "Unterkategorie bearbeiten", + "createSubcategory": "Unterkategorie erstellen", + "updateSubcategory": "Unterkategorie aktualisieren", + "subcategoryName": "Name der Unterkategorie", + "deleteSubcategory": "Unterkategorie löschen", + "subcategory": "Unterkategorie", + "categoryFirstThenBudget": "Fügen Sie zuerst eine Kategorie hinzu, um ein Budget festzulegen", + "inTheNextDays": "In {next} Tagen", + "monthlyBudget": "Monatsbudget", + "manage": "Verwalten", + "swipeLeftToDelete": "Nach links wischen zum Löschen", + "yourMonthlyBudgetWillBe": "Ihr monatliches Budget beträgt:", + "saveBudget": "Budget speichern", + "selectCategoriesToCreateBudget": "Wählen Sie Kategorien aus, um Ihr Budget zu erstellen", + "amount": "Betrag", + "addCategoryBudget": "Kategoriebudget hinzufügen", + "allCategoriesAdded": "Sie haben bereits alle verfügbaren Kategorien hinzugefügt.", + "delete": "Löschen", + "allRecurringPaymentsHere": "Alle wiederkehrenden Zahlungen werden hier angezeigt", + "addRecurringPayment": "Wiederkehrende Zahlung hinzufügen", + "seeOlderPayments": "Ältere Zahlungen ansehen", + "untilDate": "Bis {date}", + "olderPayments": "Ältere Zahlungen", + "categoryNotFound": "Kategorie nicht gefunden", + "back": "Zurück", + "onTheDay": "- Am {day}. Tag", + "noMonthlyPaymentHistory": "Kein monatlicher Zahlungsverlauf", + "noRecurrentPaymentHistory": "Kein wiederkehrender Zahlungsverlauf", + "errorLoadingPayments": "Fehler beim Laden der Zahlungen: {error}", + "editRecurringTransaction": "Wiederkehrende Transaktion bearbeiten", + "detailsExplanation": "Details (Änderungen betreffen nur zukünftige Transaktionen)", + "dateStart": "Startdatum", + "planned": "Geplant", + "composition": "Zusammensetzung", + "progress": "Fortschritt", + "noBudgetSet": "Es sind keine Budgets festgelegt", + "budgetHelpText": "Ein monatliches Budget hilft Ihnen, Ihre Ausgaben im Blick zu behalten", + "createBudget": "Budget erstellen", + "setUpTheApp": "App einrichten", + "setupDescription": "In wenigen Schritten können Sie Ihre Finanzen (fast) wie Mr. Rip verwalten.", + "startTheSetup": "Setup starten", "budgetAmount": "Budget {amount}€", - "addBudget": "Add budget", - "addBudgetForCategory": "Add budget for category {cat}", - "addCategory": "Add category", - "confirm": "Confirm", - "step1Of2": "Step 1 of 2", - "setupMonthlyBudgets": "Set up your monthly\nbudgets", - "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", - "monthlyBudgetTotal": "Monthly budget total:", - "nextStep": "Next step", - "continueWithoutBudget": "Continue without budget", - "step2Of2": "Step 2 OF 2", - "setLiquidityInMainAccount": "Set the liquidity in your main account", - "addMoreAccounts": "You'll be able to add more accounts within the app.", - "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", - "mainAccount": "Main account", - "setAmount": "Set amount", - "editIconAndColor": "Edit icon and color", - "skipStepOrStartFromZero": "Or you can skip this step and start from 0", - "startTrackingExpenses": "Start tracking your expenses", - "startFromZero": "Start from 0", + "addBudget": "Budget hinzufügen", + "addBudgetForCategory": "Budget für Kategorie {cat} hinzufügen", + "addCategory": "Kategorie hinzufügen", + "confirm": "Bestätigen", + "step1Of2": "Schritt 1 von 2", + "setupMonthlyBudgets": "Richten Sie Ihre monatlichen Budgets ein", + "chooseCategoriesForBudget": "Wählen Sie Kategorien für das Budget aus", + "monthlyBudgetTotal": "Gesamtbudget pro Monat:", + "nextStep": "Nächster Schritt", + "continueWithoutBudget": "Ohne Budget fortfahren", + "step2Of2": "Schritt 2 von 2", + "setLiquidityInMainAccount": "Liquidität im Hauptkonto festlegen", + "addMoreAccounts": "Sie können später weitere Konten hinzufügen.", + "liquidityDescription": "Dies dient als Basis für Einnahmen, Ausgaben und Vermögensberechnung.", + "mainAccount": "Hauptkonto", + "setAmount": "Betrag festlegen", + "editIconAndColor": "Symbol und Farbe bearbeiten", + "skipStepOrStartFromZero": "Oder überspringen und bei 0 beginnen", + "startTrackingExpenses": "Ausgaben tracken", + "startFromZero": "Bei 0 beginnen", "importExport": "Import/Export", - "importData": "Import data", - "importDataDescription": "Import a CSV file to update your database", - "importMoneyManager": "Import from Money Manager", - "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", - "exportData": "Export data", - "exportDataDescription": "Save your data as a CSV file", - "warningOverwrite": "Warning: Data Overwrite", - "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", - "proceedImport": "Proceed with Import", - "importSuccess": "Data imported successfully", - "exportFailed": "Export failed: {err}", - "errorExporting": "Failed to export table: {tableName}", - "errorCsvNotFound": "CSV file not found.", - "errorCsvEmpty": "The CSV file is empty.", - "errorCsvExpectedColumn": "Missing expected column: {column}", - "errorCsvUnexpectedValue": "Found an unexpected value: {value}", - "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", - "errorCsvTransactionImport": "Failed to import transaction on date: {date}", - "errorCleanDatabase": "Failed to clean the database. Reason: {error}", - "errorResetDatabase": "Failed to reset the database. Reason: {error}", - "transactionCount": "{count} transactions", - "uncategorized": "Uncategorized", - "noIncomesForSelectedMonth": "No incomes for the selected month", - "noExpensesForSelectedMonth": "No expenses for the selected month", - "total": "Total", - "noTransactionsAdded": "There are no transactions added yet", - "addTransactionCallToAction": "Add a transaction to make this section more appealing", - "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", - "availableLiquidity": "Available liquidity", - "vsLastMonth": "VS last month", - "monthlyBalance": "Monthly balance", - "currentMonth": "Current month", - "lastMonth": "Last month", - "yourAccounts": "Your accounts", - "yourBudgets": "Your budgets", - "createBudgetToTrack": "Create a budget to track your spending", - "close": "Close", - "edit": "Edit", - "errorDuplicatingTransaction": "Error duplicating transaction", - "transactionCreated": "\"{transaction}\" has been created", - "left": "Left", - "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", - "generalSettingsDesc": "Edit general settings", - "accountsDesc": "Add or edit your accounts", - "categoriesDesc": "Add/edit categories and subcategories", + "importData": "Daten importieren", + "importDataDescription": "CSV-Datei importieren, um Datenbank zu aktualisieren", + "importMoneyManager": "Von Money Manager importieren", + "importMoneyManagerDescription": "CSV von Money Manager importieren (als CSV aus XLS gespeichert).", + "exportData": "Daten exportieren", + "exportDataDescription": "Daten als CSV-Datei speichern", + "warningOverwrite": "Warnung: Daten überschreiben", + "warningOverwriteContent": "Das Importieren ersetzt alle vorhandenen Daten dauerhaft. Erstellen Sie vorher ein Backup.", + "proceedImport": "Import fortsetzen", + "importSuccess": "Daten erfolgreich importiert", + "exportFailed": "Export fehlgeschlagen: {err}", + "errorExporting": "Fehler beim Exportieren der Tabelle: {tableName}", + "errorCsvNotFound": "CSV-Datei nicht gefunden.", + "errorCsvEmpty": "Die CSV-Datei ist leer.", + "errorCsvExpectedColumn": "Erwartete Spalte fehlt: {column}", + "errorCsvUnexpectedValue": "Unerwarteter Wert gefunden: {value}", + "errorCsvImportGeneral": "Allgemeiner Fehler beim CSV-Import: {error}", + "errorCsvTransactionImport": "Fehler beim Import der Transaktion am: {date}", + "errorCleanDatabase": "Datenbankbereinigung fehlgeschlagen: {error}", + "errorResetDatabase": "Datenbank-Reset fehlgeschlagen: {error}", + "transactionCount": "{count} Transaktionen", + "uncategorized": "Nicht kategorisiert", + "noIncomesForSelectedMonth": "Keine Einnahmen für diesen Monat", + "noExpensesForSelectedMonth": "Keine Ausgaben für diesen Monat", + "total": "Gesamt", + "noTransactionsAdded": "Noch keine Transaktionen hinzugefügt", + "addTransactionCallToAction": "Fügen Sie eine Transaktion hinzu, um diesen Bereich zu füllen", + "graphsEmptyState": "Nach dem Hinzufügen von Transaktionen erscheinen hier Grafiken... wie von Zauberhand!", + "availableLiquidity": "Verfügbare Liquidität", + "vsLastMonth": "vs. letzter Monat", + "monthlyBalance": "Monatssaldo", + "currentMonth": "Aktueller Monat", + "lastMonth": "Letzter Monat", + "yourAccounts": "Ihre Konten", + "yourBudgets": "Ihre Budgets", + "createBudgetToTrack": "Budget erstellen, um Ausgaben zu verfolgen", + "close": "Schließen", + "edit": "Bearbeiten", + "errorDuplicatingTransaction": "Fehler beim Duplizieren der Transaktion", + "transactionCreated": "\"{transaction}\" wurde erstellt", + "left": "Übrig", + "notEnoughDataForGraph": "Nicht genügend Daten für die Grafik vorhanden...", + "generalSettingsDesc": "Allgemeine Einstellungen bearbeiten", + "accountsDesc": "Konten hinzufügen oder bearbeiten", + "categoriesDesc": "Kategorien und Unterkategorien verwalten", "budget": "Budget", - "budgetDesc": "Add or edit your budgets", - "importExportDesc": "Import or export data", - "notificationsDesc": "Manage your notifications settings", - "leaveFeedback": "Leave a feedback", - "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", - "appInfoDesc": "Learn more about us and the app" + "budgetDesc": "Budgets hinzufügen oder bearbeiten", + "importExportDesc": "Daten importieren oder exportieren", + "notificationsDesc": "Benachrichtigungen verwalten", + "leaveFeedback": "Feedback geben", + "leaveFeedbackDesc": "Fehler melden oder Feedback hinterlassen", + "appInfoDesc": "Mehr über uns und die App erfahren" } \ No newline at end of file diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 45c50c14..96f97e53 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -4,236 +4,236 @@ "@appName": { "description": "The name of the application" }, - "dashboard": "Dashboard", - "transactions": "Transactions", - "planning": "Planning", - "graphs": "Graphs", - "list": "List", - "categories": "Categories", - "expenses": "Expenses", - "incomes": "Incomes", - "expense": "Expense", - "income": "Income", - "transfer": "Transfer", - "accounts": "Accounts", - "details": "Details", - "account": "Account", - "category": "Category", - "date": "Date", - "investments": "Investments", - "settings": "Settings", - "notifications": "Notifications", - "settingsDisclaimer": "Open source, built by the community", - "addTransaction": "Add transaction", - "totalBalance": "Total balance", - "netWorth": "Net worth", - "save": "Save", - "cancel": "Cancel", - "success": "Success", + "dashboard": "Panel", + "transactions": "Transacciones", + "planning": "Planificación", + "graphs": "Gráficos", + "list": "Lista", + "categories": "Categorías", + "expenses": "Gastos", + "incomes": "Ingresos", + "expense": "Gasto", + "income": "Ingreso", + "transfer": "Transferencia", + "accounts": "Cuentas", + "details": "Detalles", + "account": "Cuenta", + "category": "Categoría", + "date": "Fecha", + "investments": "Inversiones", + "settings": "Ajustes", + "notifications": "Notificaciones", + "settingsDisclaimer": "Open source, desarrollada por la comunidad", + "addTransaction": "Añadir transacción", + "totalBalance": "Saldo Total", + "netWorth": "Patrimonio Neto", + "save": "Guardar", + "cancel": "Cancelar", + "success": "Éxito", "ok": "Ok", - "editingTransaction": "Editing transaction", - "newTransaction": "New transaction", - "updateTransaction": "Update transaction", - "recurringPayments": "Recurring payments", - "interval": "Interval", - "endRepetition": "End repetition", - "never": "Never", - "onADate": "On a date", - "switchDisabled": "Switch is disabled", - "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", - "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", - "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", - "storagePermissionRequired": "Storage permission is required to access your files.", - "importingData": "Importing data...", - "exportingData": "Exporting data...", - "fileSavedTo": "File saved to: {path}", - "dataImportedSuccessfully": "Data imported successfully", - "description": "Description", - "addDescription": "Add description", - "duplicateTransactionTitle": "Duplicate transaction", - "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", - "duplicate": "Duplicate", - "moreFrequent": "More frequent", - "allCategories": "All categories", - "allAccounts": "All accounts", + "editingTransaction": "Editar transacción", + "newTransaction": "Nueva transacción", + "updateTransaction": "Actualizar transacción", + "recurringPayments": "Pagos recurrentes", + "interval": "Intervalo", + "endRepetition": "Fin de la repetición", + "never": "Nunca", + "onADate": "En una fecha", + "switchDisabled": "Gestor desactivado", + "recurringTransactionWarning": "Esta es una transacción generada por una recurrente: cualquier cambio afectará solo a esta transacción.\nPara cambiar todas las transacciones futuras u opciones de recurrencia, TOCA AQUÍ.", + "saveCsvFileFailed": "No es posible guardar archivos aquí, crea o selecciona una carpeta en Descargas o Documentos. Error: {e}", + "errorPickingFile": "Error al seleccionar el archivo. Asegúrate de tener los permisos necesarios. Error: {error}", + "storagePermissionRequired": "Se requiere permiso de almacenamiento para acceder a los archivos.", + "importingData": "Importando datos...", + "exportingData": "Exportando datos...", + "fileSavedTo": "Archivo guardado en: {path}", + "dataImportedSuccessfully": "Datos importados con éxito", + "description": "Descripción", + "addDescription": "Añadir descripción", + "duplicateTransactionTitle": "Duplicar transacción", + "duplicateTransactionContent": "Esta transacción ya está en la lista. ¿Deseas duplicarla? Podrás editar la nueva entrada posteriormente.", + "duplicate": "Duplicar", + "moreFrequent": "Más frecuente", + "allCategories": "Todas las categorías", + "allAccounts": "Todas las cuentas", "errorOccurred": "Error: {err}", - "selectAccount": "Select Account", - "to": "To:", - "from": "From:", - "recurringTransactionAdded": "Recurring transaction added", - "recurringTransactions": "Recurring transactions", - "addTransactionReminder": "Add transaction reminder", - "privacyPolicyTitle": "Privacy Policy", - "privacyCollectTitle": "What Information Do We Collect?", - "privacyChangesTitle": "Changes to This Privacy Policy", - "contactUsTitle": "Contact us", - "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", - "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", - "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", - "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", - "collaboratorsTitle": "Collaborators", - "meetTheTeam": "Meet the team", - "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", - "wantToContribute": "Want to contribute?", - "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", - "appInfo": "App Info", - "appVersion": "App Version:", - "collaborators": "Collaborators", - "collaboratorsDescription": "See the team behind this app", - "privacyPolicy": "Privacy Policy", - "privacyPolicyDescription": "Read more", - "generalSettings": "General Settings", - "appearance": "Appearance", - "currency": "Currency", - "requireAuthentication": "Require authentication", - "searchForATransaction": "Search for a transaction", - "selectACurrency": "Select a currency", - "search": "Search", - "searchIn": "Search in", - "lastTransactions": "Your last transactions", - "startReconciliation": "Start reconciliation", - "newBalance": "New balance", - "balanceDiscrepancy": "Balance Discrepancy?", - "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", - "newAccount": "New account", - "editAccount": "Edit account", - "createAccount": "Create account", - "accountName": "Account name", - "name": "Name", - "iconAndColor": "Icon and color", - "chooseColor": "Choose color", - "chooseIcon": "Choose icon", - "done": "Fatto", - "add": "Add", - "setAsMainAccount": "Set as main account", - "countsForNetWorth": "Counts for the net worth", - "deleteAccount": "Delete account", - "initialBalance": "Initial balance", - "currentBalance": "Current balance", - "showLess": "Show less", - "showMore": "Show more", - "addSubcategory": "Add subcategory", - "newCategory": "New category", - "editCategory": "Edit category", - "createCategory": "Create category", - "updateCategory": "Update category", - "categoryName": "Category name", - "type": "Type", - "deleteCategory": "Delete category", - "newSubcategory": "New subcategory", - "editSubcategory": "Edit subcategory", - "createSubcategory": "Create subcategory", - "updateSubcategory": "Update subcategory", - "subcategoryName": "Subcategory name", - "deleteSubcategory": "Delete subcategory", - "subcategory": "Subcategory", - "categoryFirstThenBudget": "Add a category first to set a budget", - "inTheNextDays": "In {next} days", - "monthlyBudget": "Monthly budget", - "manage": "Manage", - "swipeLeftToDelete": "Swipe left to delete", - "yourMonthlyBudgetWillBe": "Your monthly budget will be:", - "saveBudget": "Save budget", - "selectCategoriesToCreateBudget": "Select the categories to create your budget", - "amount": "Amount", - "addCategoryBudget": "Add category budget", - "allCategoriesAdded": "You have already added all available categories.", - "delete": "Delete", - "allRecurringPaymentsHere": "All recurring payments will be displayed here", - "addRecurringPayment": "Add recurring payment", - "seeOlderPayments": "See older payments", - "untilDate": "Until {date}", - "olderPayments": "Older payments", - "categoryNotFound": "Category not found", - "back": "Back", - "onTheDay": "- On the {day} day", - "noMonthlyPaymentHistory": "No monthly payment history", - "noRecurrentPaymentHistory": "No recurrent payment history", - "errorLoadingPayments": "Error loading payments: {error}", - "editRecurringTransaction": "Edit recurring transaction", - "detailsExplanation": "Details (any change will affect only future transactions)", - "dateStart": "Date start", - "planned": "Planned", - "composition": "Composition", - "progress": "Progress", - "noBudgetSet": "There are no budgets set", - "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", - "createBudget": "Create budget", - "setUpTheApp": "Set up the app", - "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", - "startTheSetup": "Start the setup", - "budgetAmount": "Budget {amount}€", - "addBudget": "Add budget", - "addBudgetForCategory": "Add budget for category {cat}", - "addCategory": "Add category", - "confirm": "Confirm", - "step1Of2": "Step 1 of 2", - "setupMonthlyBudgets": "Set up your monthly\nbudgets", - "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", - "monthlyBudgetTotal": "Monthly budget total:", - "nextStep": "Next step", - "continueWithoutBudget": "Continue without budget", - "step2Of2": "Step 2 OF 2", - "setLiquidityInMainAccount": "Set the liquidity in your main account", - "addMoreAccounts": "You'll be able to add more accounts within the app.", - "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", - "mainAccount": "Main account", - "setAmount": "Set amount", - "editIconAndColor": "Edit icon and color", - "skipStepOrStartFromZero": "Or you can skip this step and start from 0", - "startTrackingExpenses": "Start tracking your expenses", - "startFromZero": "Start from 0", - "importExport": "Import/Export", - "importData": "Import data", - "importDataDescription": "Import a CSV file to update your database", - "importMoneyManager": "Import from Money Manager", - "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", - "exportData": "Export data", - "exportDataDescription": "Save your data as a CSV file", - "warningOverwrite": "Warning: Data Overwrite", - "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", - "proceedImport": "Proceed with Import", - "importSuccess": "Data imported successfully", - "exportFailed": "Export failed: {err}", - "errorExporting": "Failed to export table: {tableName}", - "errorCsvNotFound": "CSV file not found.", - "errorCsvEmpty": "The CSV file is empty.", - "errorCsvExpectedColumn": "Missing expected column: {column}", - "errorCsvUnexpectedValue": "Found an unexpected value: {value}", - "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", - "errorCsvTransactionImport": "Failed to import transaction on date: {date}", - "errorCleanDatabase": "Failed to clean the database. Reason: {error}", - "errorResetDatabase": "Failed to reset the database. Reason: {error}", - "transactionCount": "{count} transactions", - "uncategorized": "Uncategorized", - "noIncomesForSelectedMonth": "No incomes for the selected month", - "noExpensesForSelectedMonth": "No expenses for the selected month", + "selectAccount": "Seleccionar cuenta", + "to": "Para:", + "from": "De:", + "recurringTransactionAdded": "Transacción recurrente añadida", + "recurringTransactions": "Transacciones recurrentes", + "addTransactionReminder": "Añadir recordatorio de transacción", + "privacyPolicyTitle": "Política de Privacidad", + "privacyCollectTitle": "¿Qué información recopilamos?", + "privacyChangesTitle": "Cambios en la Política de Privacidad", + "contactUsTitle": "Contáctanos", + "privacyIntro": "Sossoldi está desarrollado como una aplicación open source. Este servicio se proporciona de forma gratuita y está destinado a ser usado tal cual.\nNo tenemos interés en recopilar ninguna información personal. Creemos que dicha información te pertenece solo a ti. No almacenamos ni transmitimos tus datos personales, ni incluimos software de publicidad o análisis que se comunique con terceros.\n", + "privacyCollectBody": "Sossoldi no recopila ninguna información personal ni se conecta a Internet. Cualquier información añadida a la aplicación existe exclusivamente en tu dispositivo y en ningún otro lugar.\n", + "privacyChangesBody": "Podemos actualizar nuestra Política de Privacidad ocasionalmente. Por tanto, recomendamos que revises esta página periódicamente para verificar cambios.\nEsta política entra en vigor a partir del 01/01/2024.\n", + "contactUsBody": "Si tienes dudas o sugerencias sobre nuestra Política de Privacidad, no dudes en contactarnos en\n", + "collaboratorsTitle": "Colaboradores", + "meetTheTeam": "Conoce al equipo", + "teamDescription": "Sossoldi es desarrollada y mantenida por una apasionada comunidad open source. Cada funcionalidad, corrección e idea viene de personas como tú.", + "wantToContribute": "¿Quieres contribuir?", + "contributeDescription": "Abre un issue, envía un PR o simplemente saluda en GitHub", + "appInfo": "Información de la app", + "appVersion": "Versión de la app:", + "collaborators": "Colaboradores", + "collaboratorsDescription": "Conoce al equipo detrás de esta app", + "privacyPolicy": "Política de Privacidad", + "privacyPolicyDescription": "Saber más", + "generalSettings": "Ajustes generales", + "appearance": "Apariencia", + "currency": "Moneda", + "requireAuthentication": "Requerir autenticación", + "searchForATransaction": "Buscar una transacción", + "selectACurrency": "Seleccionar una moneda", + "search": "Buscar", + "searchIn": "Buscar en", + "lastTransactions": "Tus últimas transacciones", + "startReconciliation": "Iniciar conciliación", + "newBalance": "Nuevo saldo", + "balanceDiscrepancy": "¿Diferencia de saldo?", + "balanceAdjustmentHint": "El saldo registrado puede diferir del extracto bancario. Toca abajo para ajustar el saldo manualmente y mantener tus registros actualizados.", + "newAccount": "Nueva cuenta", + "editAccount": "Editar cuenta", + "createAccount": "Crear cuenta", + "accountName": "Nombre de la cuenta", + "name": "Nombre", + "iconAndColor": "Icono y color", + "chooseColor": "Elegir color", + "chooseIcon": "Elegir icono", + "done": "Hecho", + "add": "Añadir", + "setAsMainAccount": "Definir como cuenta principal", + "countsForNetWorth": "Incluir en el patrimonio neto", + "deleteAccount": "Eliminar cuenta", + "initialBalance": "Saldo inicial", + "currentBalance": "Saldo actual", + "showLess": "Mostrar menos", + "showMore": "Mostrar más", + "addSubcategory": "Añadir subcategoría", + "newCategory": "Nueva categoría", + "editCategory": "Editar categoría", + "createCategory": "Crear categoría", + "updateCategory": "Actualizar categoría", + "categoryName": "Nombre de la categoría", + "type": "Tipo", + "deleteCategory": "Eliminar categoría", + "newSubcategory": "Nueva subcategoría", + "editSubcategory": "Editar subcategoría", + "createSubcategory": "Crear subcategoría", + "updateSubcategory": "Actualizar subcategoría", + "subcategoryName": "Nombre de la subcategoría", + "deleteSubcategory": "Eliminar subcategoría", + "subcategory": "Subcategoría", + "categoryFirstThenBudget": "Añade una categoría antes de crear un presupuesto", + "inTheNextDays": "En los próximos {next} días", + "monthlyBudget": "Presupuesto mensual", + "manage": "Gestionar", + "swipeLeftToDelete": "Desliza a la izquierda para eliminar", + "yourMonthlyBudgetWillBe": "Tu presupuesto mensual será:", + "saveBudget": "Guardar presupuesto", + "selectCategoriesToCreateBudget": "Selecciona las categorías para crear tu presupuesto", + "amount": "Valor", + "addCategoryBudget": "Añadir presupuesto a la categoría", + "allCategoriesAdded": "Ya has añadido todas las categorías disponibles.", + "delete": "Eliminar", + "allRecurringPaymentsHere": "Todos los pagos recurrentes se mostrarán aquí", + "addRecurringPayment": "Añadir pago recurrente", + "seeOlderPayments": "Ver pagos antiguos", + "untilDate": "Hasta {date}", + "olderPayments": "Pagos antiguos", + "categoryNotFound": "Categoría no encontrada", + "back": "Volver", + "onTheDay": "- El día {day}", + "noMonthlyPaymentHistory": "Sin historial de pagos mensuales", + "noRecurrentPaymentHistory": "Sin historial de pagos recurrentes", + "errorLoadingPayments": "Error al cargar pagos: {error}", + "editRecurringTransaction": "Editar transacción recurrente", + "detailsExplanation": "Detalles (cualquier cambio afectará solo a transacciones futuras)", + "dateStart": "Fecha de inicio", + "planned": "Planeado", + "composition": "Composición", + "progress": "Progreso", + "noBudgetSet": "Ningún presupuesto definido", + "budgetHelpText": "Un presupuesto mensual puede ayudarte a realizar un seguimiento de tus gastos y mantenerte dentro de los límites", + "createBudget": "Crear presupuesto", + "setUpTheApp": "Configurar la app", + "setupDescription": "En algunos pasos estarás listo para empezar a\nrealizar un seguimiento de tus finanzas personales (casi)\ncomo Mr. Rip.", + "startTheSetup": "Iniciar configuración", + "budgetAmount": "Presupuesto {amount}€", + "addBudget": "Añadir presupuesto", + "addBudgetForCategory": "Añadir presupuesto para la categoría {cat}", + "addCategory": "Añadir categoría", + "confirm": "Confirmar", + "step1Of2": "Paso 1 de 2", + "setupMonthlyBudgets": "Define tus presupuestos\nmensuales", + "chooseCategoriesForBudget": "Elige las categorías para las que deseas definir un presupuesto", + "monthlyBudgetTotal": "Total del presupuesto mensual:", + "nextStep": "Próximo paso", + "continueWithoutBudget": "Continuar sin presupuesto", + "step2Of2": "Paso 2 de 2", + "setLiquidityInMainAccount": "Define la liquidez en tu cuenta principal", + "addMoreAccounts": "Podrás añadir más cuentas dentro de la app", + "liquidityDescription": "Se usará como base para añadir ingresos, gastos y calcular tu patrimonio.\nPodrás añadir más cuentas dentro de la app.", + "mainAccount": "Cuenta principal", + "setAmount": "Definir valor", + "editIconAndColor": "Editar icono y color", + "skipStepOrStartFromZero": "O puedes saltar este paso y empezar desde 0", + "startTrackingExpenses": "Empezar a realizar un seguimiento de tus gastos", + "startFromZero": "Empezar desde 0", + "importExport": "Importar/Exportar", + "importData": "Importar datos", + "importDataDescription": "Importa un archivo CSV para actualizar la base de datos", + "importMoneyManager": "Importar de Money Manager", + "importMoneyManagerDescription": "Importa CSV de Money Manager para actualizar la base de datos. El archivo debe guardarse en formato CSV desde XLS.", + "exportData": "Exportar datos", + "exportDataDescription": "Guarda tus datos como un archivo CSV", + "warningOverwrite": "Atención: Sobrescritura de datos", + "warningOverwriteContent": "La importación de este archivo reemplazará permanentemente tus datos existentes. Esta acción no se puede deshacer. Asegúrate de tener una copia de seguridad antes de proceder.", + "proceedImport": "Proceder con la importación", + "importSuccess": "Datos importados con éxito", + "exportFailed": "Fallo en la exportación: {err}", + "errorExporting": "No fue posible exportar la tabla: {tableName}", + "errorCsvNotFound": "Archivo CSV no encontrado.", + "errorCsvEmpty": "El archivo CSV está vacío.", + "errorCsvExpectedColumn": "Columna faltando en el CSV: {column}", + "errorCsvUnexpectedValue": "Valor inesperado encontrado: {value}", + "errorCsvImportGeneral": "Ocurrió un error general durante la importación del CSV. Error: {error}", + "errorCsvTransactionImport": "Error al importar la transacción en la fecha: {date}", + "errorCleanDatabase": "No fue posible limpiar la base de datos. Motivo: {error}", + "errorResetDatabase": "No fue posible restablecer la base de datos. Motivo: {error}", + "transactionCount": "{count} transacciones", + "uncategorized": "Sin categoría", + "noIncomesForSelectedMonth": "Ningún ingreso para el mes seleccionado", + "noExpensesForSelectedMonth": "Ningún gasto para el mes seleccionado", "total": "Total", - "noTransactionsAdded": "There are no transactions added yet", - "addTransactionCallToAction": "Add a transaction to make this section more appealing", - "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", - "availableLiquidity": "Available liquidity", - "vsLastMonth": "VS last month", - "monthlyBalance": "Monthly balance", - "currentMonth": "Current month", - "lastMonth": "Last month", - "yourAccounts": "Your accounts", - "yourBudgets": "Your budgets", - "createBudgetToTrack": "Create a budget to track your spending", - "close": "Close", - "edit": "Edit", - "errorDuplicatingTransaction": "Error duplicating transaction", - "transactionCreated": "\"{transaction}\" has been created", - "left": "Left", - "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", - "generalSettingsDesc": "Edit general settings", - "accountsDesc": "Add or edit your accounts", - "categoriesDesc": "Add/edit categories and subcategories", - "budget": "Budget", - "budgetDesc": "Add or edit your budgets", - "importExportDesc": "Import or export data", - "notificationsDesc": "Manage your notifications settings", - "leaveFeedback": "Leave a feedback", - "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", - "appInfoDesc": "Learn more about us and the app" + "noTransactionsAdded": "Ninguna transacción añadida aún", + "addTransactionCallToAction": "Añade una transacción para hacer esta sección más interesante", + "graphsEmptyState": "Después de añadir algunas transacciones, gráficos increíbles aparecerán aquí... ¡casi como por arte de magia!", + "availableLiquidity": "Liquidez disponible", + "vsLastMonth": "VS mes anterior", + "monthlyBalance": "Saldo mensual", + "currentMonth": "Mes actual", + "lastMonth": "Mes anterior", + "yourAccounts": "Tus cuentas", + "yourBudgets": "Tus presupuestos", + "createBudgetToTrack": "Crea un presupuesto para realizar un seguimiento de tus gastos", + "close": "Cerrar", + "edit": "Editar", + "errorDuplicatingTransaction": "Error al duplicar la transacción", + "transactionCreated": "\"{transaction}\" fue creada", + "left": "Restante", + "notEnoughDataForGraph": "Lamentamos que no haya\nsuficientes datos para crear el gráfico...", + "generalSettingsDesc": "Editar ajustes generales", + "accountsDesc": "Añadir o editar tus cuentas", + "categoriesDesc": "Añadir/editar categorías y subcategorías", + "budget": "Presupuesto", + "budgetDesc": "Añadir o editar tus presupuestos", + "importExportDesc": "Importar o exportar datos", + "notificationsDesc": "Gestionar tus ajustes de notificación", + "leaveFeedback": "Dejar comentarios", + "leaveFeedbackDesc": "Completa un pequeño formulario para reportar un error o dejar comentarios", + "appInfoDesc": "Saber más sobre nosotros y la app" } \ No newline at end of file diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 291b3053..eb726fca 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -15,507 +15,512 @@ class AppLocalizationsDe extends AppLocalizations { String get dashboard => 'Dashboard'; @override - String get transactions => 'Transactions'; + String get transactions => 'Transaktionen'; @override - String get planning => 'Planning'; + String get planning => 'Planung'; @override - String get graphs => 'Graphs'; + String get graphs => 'Grafiken'; @override - String get list => 'List'; + String get list => 'Liste'; @override - String get categories => 'Categories'; + String get categories => 'Kategorien'; @override - String get expenses => 'Expenses'; + String get expenses => 'Ausgaben'; @override - String get incomes => 'Incomes'; + String get incomes => 'Einnahmen'; @override - String get expense => 'Expense'; + String get expense => 'Ausgabe'; @override - String get income => 'Income'; + String get income => 'Einnahme'; @override - String get transfer => 'Transfer'; + String get transfer => 'Überweisung'; @override - String get accounts => 'Accounts'; + String get accounts => 'Konten'; @override String get details => 'Details'; @override - String get account => 'Account'; + String get account => 'Konto'; @override - String get category => 'Category'; + String get category => 'Kategorie'; @override - String get date => 'Date'; + String get date => 'Datum'; @override - String get investments => 'Investments'; + String get investments => 'Investitionen'; @override - String get settings => 'Settings'; + String get settings => 'Einstellungen'; @override - String get notifications => 'Notifications'; + String get notifications => 'Benachrichtigungen'; @override - String get settingsDisclaimer => 'Open source, built by the community'; + String get settingsDisclaimer => 'Open Source, von der Community entwickelt'; @override - String get addTransaction => 'Add transaction'; + String get addTransaction => 'Transaktion hinzufügen'; @override - String get totalBalance => 'Total balance'; + String get totalBalance => 'Gesamtsaldo'; @override - String get netWorth => 'Net worth'; + String get netWorth => 'Nettovermögen'; @override - String get save => 'Save'; + String get save => 'Speichern'; @override - String get cancel => 'Cancel'; + String get cancel => 'Abbrechen'; @override - String get success => 'Success'; + String get success => 'Erfolg'; @override String get ok => 'Ok'; @override - String get editingTransaction => 'Editing transaction'; + String get editingTransaction => 'Transaktion bearbeiten'; @override - String get newTransaction => 'New transaction'; + String get newTransaction => 'Neue Transaktion'; @override - String get updateTransaction => 'Update transaction'; + String get updateTransaction => 'Transaktion aktualisieren'; @override - String get recurringPayments => 'Recurring payments'; + String get recurringPayments => 'Wiederkehrende Zahlungen'; @override - String get interval => 'Interval'; + String get interval => 'Intervall'; @override - String get endRepetition => 'End repetition'; + String get endRepetition => 'Wiederholung beenden'; @override - String get never => 'Never'; + String get never => 'Nie'; @override - String get onADate => 'On a date'; + String get onADate => 'An einem Datum'; @override - String get switchDisabled => 'Switch is disabled'; + String get switchDisabled => 'Schalter ist deaktiviert'; @override String get recurringTransactionWarning => - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + 'Dies ist eine von einer wiederkehrenden Transaktion generierte Buchung: Jede Änderung betrifft nur diese einzelne Transaktion.\nUm alle zukünftigen Transaktionen oder Wiederholungsoptionen zu ändern, HIER TIPPEN.'; @override String saveCsvFileFailed(Object e) { - return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + return 'Datei kann hier nicht gespeichert werden. Bitte wählen Sie einen Ordner in Downloads oder Dokumente. Fehler: $e'; } @override String errorPickingFile(Object error) { - return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + return 'Fehler beim Auswählen der Datei. Bitte stellen Sie sicher, dass Sie über ausreichende Berechtigungen verfügen. Fehler: $error'; } @override String get storagePermissionRequired => - 'Storage permission is required to access your files.'; + 'Speicherberechtigung ist erforderlich, um auf Ihre Dateien zuzugreifen.'; @override - String get importingData => 'Importing data...'; + String get importingData => 'Daten werden importiert...'; @override - String get exportingData => 'Exporting data...'; + String get exportingData => 'Daten werden exportiert...'; @override String fileSavedTo(Object path) { - return 'File saved to: $path'; + return 'Datei gespeichert unter: $path'; } @override - String get dataImportedSuccessfully => 'Data imported successfully'; + String get dataImportedSuccessfully => 'Daten erfolgreich importiert'; @override - String get description => 'Description'; + String get description => 'Beschreibung'; @override - String get addDescription => 'Add description'; + String get addDescription => 'Beschreibung hinzufügen'; @override - String get duplicateTransactionTitle => 'Duplicate transaction'; + String get duplicateTransactionTitle => 'Transaktion duplizieren'; @override String get duplicateTransactionContent => - 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + 'Diese Transaktion ist bereits in der Liste. Möchten Sie sie duplizieren? Sie können die neue Transaktion anschließend bearbeiten.'; @override - String get duplicate => 'Duplicate'; + String get duplicate => 'Duplizieren'; @override - String get moreFrequent => 'More frequent'; + String get moreFrequent => 'Häufiger'; @override - String get allCategories => 'All categories'; + String get allCategories => 'Alle Kategorien'; @override - String get allAccounts => 'All accounts'; + String get allAccounts => 'Alle Konten'; @override String errorOccurred(Object err) { - return 'Error: $err'; + return 'Fehler: $err'; } @override - String get selectAccount => 'Select Account'; + String get selectAccount => 'Konto auswählen'; @override - String get to => 'To:'; + String get to => 'An:'; @override - String get from => 'From:'; + String get from => 'Von:'; @override - String get recurringTransactionAdded => 'Recurring transaction added'; + String get recurringTransactionAdded => + 'Wiederkehrende Transaktion hinzugefügt'; @override - String get recurringTransactions => 'Recurring transactions'; + String get recurringTransactions => 'Wiederkehrende Transaktionen'; @override - String get addTransactionReminder => 'Add transaction reminder'; + String get addTransactionReminder => 'Transaktionserinnerung hinzufügen'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Datenschutzerklärung'; @override - String get privacyCollectTitle => 'What Information Do We Collect?'; + String get privacyCollectTitle => 'Welche Informationen sammeln wir?'; @override - String get privacyChangesTitle => 'Changes to This Privacy Policy'; + String get privacyChangesTitle => 'Änderungen an dieser Datenschutzerklärung'; @override - String get contactUsTitle => 'Contact us'; + String get contactUsTitle => 'Kontaktieren Sie uns'; @override String get privacyIntro => - 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + 'Sossoldi ist als Open-Source-App konzipiert. Dieser Dienst wird von uns kostenlos zur Verfügung gestellt und ist für die Nutzung im Ist-Zustand bestimmt.\nWir sind nicht daran interessiert, persönliche Informationen zu sammeln. Wir glauben, dass diese Informationen ausschließlich Ihnen gehören. Wir speichern oder übertragen Ihre persönlichen Daten nicht und binden keine Werbe- oder Analyse-Software ein, die mit Dritten kommuniziert.\n'; @override String get privacyCollectBody => - 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + 'Sossoldi sammelt keine persönlichen Daten und verbindet sich nicht mit dem Internet. Alle Informationen, die Sie in der App hinzufügen, existieren ausschließlich auf Ihrem Gerät und nirgendwo sonst.\n'; @override String get privacyChangesBody => - 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + 'Wir können unsere Datenschutzerklärung von Zeit zu Zeit aktualisieren. Daher wird empfohlen, diese Seite regelmäßig auf Änderungen zu überprüfen.\nDiese Richtlinie ist gültig ab 2024-01-01.\n'; @override String get contactUsBody => - 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + 'Wenn Sie Fragen oder Anregungen zu unserer Datenschutzerklärung haben, zögern Sie nicht, uns zu kontaktieren unter \n'; @override - String get collaboratorsTitle => 'Collaborators'; + String get collaboratorsTitle => 'Mitwirkende'; @override - String get meetTheTeam => 'Meet the team'; + String get meetTheTeam => 'Das Team'; @override String get teamDescription => - 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + 'Sossoldi wird von einer leidenschaftlichen Open-Source-Community entwickelt und gepflegt. Jede Funktion, jeder Fix und jede Idee kommt von Menschen wie Ihnen.'; @override - String get wantToContribute => 'Want to contribute?'; + String get wantToContribute => 'Möchten Sie mitwirken?'; @override String get contributeDescription => - 'Open an issue, submit a PR or just say hi on GitHub'; + 'Öffnen Sie ein Issue, senden Sie einen PR oder sagen Sie einfach Hallo auf GitHub'; @override - String get appInfo => 'App Info'; + String get appInfo => 'App-Info'; @override - String get appVersion => 'App Version:'; + String get appVersion => 'App-Version:'; @override - String get collaborators => 'Collaborators'; + String get collaborators => 'Mitwirkende'; @override - String get collaboratorsDescription => 'See the team behind this app'; + String get collaboratorsDescription => + 'Sehen Sie sich das Team hinter dieser App an'; @override - String get privacyPolicy => 'Privacy Policy'; + String get privacyPolicy => 'Datenschutzerklärung'; @override - String get privacyPolicyDescription => 'Read more'; + String get privacyPolicyDescription => 'Mehr lesen'; @override - String get generalSettings => 'General Settings'; + String get generalSettings => 'Allgemeine Einstellungen'; @override - String get appearance => 'Appearance'; + String get appearance => 'Erscheinungsbild'; @override - String get currency => 'Currency'; + String get currency => 'Währung'; @override - String get requireAuthentication => 'Require authentication'; + String get requireAuthentication => 'Authentifizierung anfordern'; @override - String get searchForATransaction => 'Search for a transaction'; + String get searchForATransaction => 'Nach einer Transaktion suchen'; @override - String get selectACurrency => 'Select a currency'; + String get selectACurrency => 'Währung auswählen'; @override - String get search => 'Search'; + String get search => 'Suche'; @override - String get searchIn => 'Search in'; + String get searchIn => 'Suchen in'; @override - String get lastTransactions => 'Your last transactions'; + String get lastTransactions => 'Ihre letzten Transaktionen'; @override - String get startReconciliation => 'Start reconciliation'; + String get startReconciliation => 'Abgleich starten'; @override - String get newBalance => 'New balance'; + String get newBalance => 'Neuer Kontostand'; @override - String get balanceDiscrepancy => 'Balance Discrepancy?'; + String get balanceDiscrepancy => 'Saldo-Diskrepanz?'; @override String get balanceAdjustmentHint => - 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + 'Ihr erfasster Saldo kann von Ihrem Bankbeleg abweichen. Tippen Sie unten, um Ihren Saldo manuell anzupassen.'; @override - String get newAccount => 'New account'; + String get newAccount => 'Neues Konto'; @override - String get editAccount => 'Edit account'; + String get editAccount => 'Konto bearbeiten'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Konto erstellen'; @override - String get accountName => 'Account name'; + String get accountName => 'Kontoname'; @override String get name => 'Name'; @override - String get iconAndColor => 'Icon and color'; + String get iconAndColor => 'Symbol und Farbe'; @override - String get chooseColor => 'Choose color'; + String get chooseColor => 'Farbe wählen'; @override - String get chooseIcon => 'Choose icon'; + String get chooseIcon => 'Symbol wählen'; @override - String get done => 'Fatto'; + String get done => 'Fertig'; @override - String get add => 'Add'; + String get add => 'Hinzufügen'; @override - String get setAsMainAccount => 'Set as main account'; + String get setAsMainAccount => 'Als Hauptkonto festlegen'; @override - String get countsForNetWorth => 'Counts for the net worth'; + String get countsForNetWorth => 'Zählt für das Nettovermögen'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Konto löschen'; @override - String get initialBalance => 'Initial balance'; + String get initialBalance => 'Anfangssaldo'; @override - String get currentBalance => 'Current balance'; + String get currentBalance => 'Aktueller Saldo'; @override - String get showLess => 'Show less'; + String get showLess => 'Weniger anzeigen'; @override - String get showMore => 'Show more'; + String get showMore => 'Mehr anzeigen'; @override - String get addSubcategory => 'Add subcategory'; + String get addSubcategory => 'Unterkategorie hinzufügen'; @override - String get newCategory => 'New category'; + String get newCategory => 'Neue Kategorie'; @override - String get editCategory => 'Edit category'; + String get editCategory => 'Kategorie bearbeiten'; @override - String get createCategory => 'Create category'; + String get createCategory => 'Kategorie erstellen'; @override - String get updateCategory => 'Update category'; + String get updateCategory => 'Kategorie aktualisieren'; @override - String get categoryName => 'Category name'; + String get categoryName => 'Kategoriename'; @override - String get type => 'Type'; + String get type => 'Typ'; @override - String get deleteCategory => 'Delete category'; + String get deleteCategory => 'Kategorie löschen'; @override - String get newSubcategory => 'New subcategory'; + String get newSubcategory => 'Neue Unterkategorie'; @override - String get editSubcategory => 'Edit subcategory'; + String get editSubcategory => 'Unterkategorie bearbeiten'; @override - String get createSubcategory => 'Create subcategory'; + String get createSubcategory => 'Unterkategorie erstellen'; @override - String get updateSubcategory => 'Update subcategory'; + String get updateSubcategory => 'Unterkategorie aktualisieren'; @override - String get subcategoryName => 'Subcategory name'; + String get subcategoryName => 'Name der Unterkategorie'; @override - String get deleteSubcategory => 'Delete subcategory'; + String get deleteSubcategory => 'Unterkategorie löschen'; @override - String get subcategory => 'Subcategory'; + String get subcategory => 'Unterkategorie'; @override - String get categoryFirstThenBudget => 'Add a category first to set a budget'; + String get categoryFirstThenBudget => + 'Fügen Sie zuerst eine Kategorie hinzu, um ein Budget festzulegen'; @override String inTheNextDays(Object next) { - return 'In $next days'; + return 'In $next Tagen'; } @override - String get monthlyBudget => 'Monthly budget'; + String get monthlyBudget => 'Monatsbudget'; @override - String get manage => 'Manage'; + String get manage => 'Verwalten'; @override - String get swipeLeftToDelete => 'Swipe left to delete'; + String get swipeLeftToDelete => 'Nach links wischen zum Löschen'; @override - String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + String get yourMonthlyBudgetWillBe => 'Ihr monatliches Budget beträgt:'; @override - String get saveBudget => 'Save budget'; + String get saveBudget => 'Budget speichern'; @override String get selectCategoriesToCreateBudget => - 'Select the categories to create your budget'; + 'Wählen Sie Kategorien aus, um Ihr Budget zu erstellen'; @override - String get amount => 'Amount'; + String get amount => 'Betrag'; @override - String get addCategoryBudget => 'Add category budget'; + String get addCategoryBudget => 'Kategoriebudget hinzufügen'; @override String get allCategoriesAdded => - 'You have already added all available categories.'; + 'Sie haben bereits alle verfügbaren Kategorien hinzugefügt.'; @override - String get delete => 'Delete'; + String get delete => 'Löschen'; @override String get allRecurringPaymentsHere => - 'All recurring payments will be displayed here'; + 'Alle wiederkehrenden Zahlungen werden hier angezeigt'; @override - String get addRecurringPayment => 'Add recurring payment'; + String get addRecurringPayment => 'Wiederkehrende Zahlung hinzufügen'; @override - String get seeOlderPayments => 'See older payments'; + String get seeOlderPayments => 'Ältere Zahlungen ansehen'; @override String untilDate(Object date) { - return 'Until $date'; + return 'Bis $date'; } @override - String get olderPayments => 'Older payments'; + String get olderPayments => 'Ältere Zahlungen'; @override - String get categoryNotFound => 'Category not found'; + String get categoryNotFound => 'Kategorie nicht gefunden'; @override - String get back => 'Back'; + String get back => 'Zurück'; @override String onTheDay(Object day) { - return '- On the $day day'; + return '- Am $day. Tag'; } @override - String get noMonthlyPaymentHistory => 'No monthly payment history'; + String get noMonthlyPaymentHistory => 'Kein monatlicher Zahlungsverlauf'; @override - String get noRecurrentPaymentHistory => 'No recurrent payment history'; + String get noRecurrentPaymentHistory => + 'Kein wiederkehrender Zahlungsverlauf'; @override String errorLoadingPayments(Object error) { - return 'Error loading payments: $error'; + return 'Fehler beim Laden der Zahlungen: $error'; } @override - String get editRecurringTransaction => 'Edit recurring transaction'; + String get editRecurringTransaction => + 'Wiederkehrende Transaktion bearbeiten'; @override String get detailsExplanation => - 'Details (any change will affect only future transactions)'; + 'Details (Änderungen betreffen nur zukünftige Transaktionen)'; @override - String get dateStart => 'Date start'; + String get dateStart => 'Startdatum'; @override - String get planned => 'Planned'; + String get planned => 'Geplant'; @override - String get composition => 'Composition'; + String get composition => 'Zusammensetzung'; @override - String get progress => 'Progress'; + String get progress => 'Fortschritt'; @override - String get noBudgetSet => 'There are no budgets set'; + String get noBudgetSet => 'Es sind keine Budgets festgelegt'; @override String get budgetHelpText => - 'A monthly budget can help you keep track of your expenses and stay within the limits'; + 'Ein monatliches Budget hilft Ihnen, Ihre Ausgaben im Blick zu behalten'; @override - String get createBudget => 'Create budget'; + String get createBudget => 'Budget erstellen'; @override - String get setUpTheApp => 'Set up the app'; + String get setUpTheApp => 'App einrichten'; @override String get setupDescription => - 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + 'In wenigen Schritten können Sie Ihre Finanzen (fast) wie Mr. Rip verwalten.'; @override - String get startTheSetup => 'Start the setup'; + String get startTheSetup => 'Setup starten'; @override String budgetAmount(Object amount) { @@ -523,255 +528,253 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get addBudget => 'Add budget'; + String get addBudget => 'Budget hinzufügen'; @override String addBudgetForCategory(Object cat) { - return 'Add budget for category $cat'; + return 'Budget für Kategorie $cat hinzufügen'; } @override - String get addCategory => 'Add category'; + String get addCategory => 'Kategorie hinzufügen'; @override - String get confirm => 'Confirm'; + String get confirm => 'Bestätigen'; @override - String get step1Of2 => 'Step 1 of 2'; + String get step1Of2 => 'Schritt 1 von 2'; @override - String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + String get setupMonthlyBudgets => 'Richten Sie Ihre monatlichen Budgets ein'; @override String get chooseCategoriesForBudget => - 'Choose which categories you want to set a budget for'; + 'Wählen Sie Kategorien für das Budget aus'; @override - String get monthlyBudgetTotal => 'Monthly budget total:'; + String get monthlyBudgetTotal => 'Gesamtbudget pro Monat:'; @override - String get nextStep => 'Next step'; + String get nextStep => 'Nächster Schritt'; @override - String get continueWithoutBudget => 'Continue without budget'; + String get continueWithoutBudget => 'Ohne Budget fortfahren'; @override - String get step2Of2 => 'Step 2 OF 2'; + String get step2Of2 => 'Schritt 2 von 2'; @override - String get setLiquidityInMainAccount => - 'Set the liquidity in your main account'; + String get setLiquidityInMainAccount => 'Liquidität im Hauptkonto festlegen'; @override - String get addMoreAccounts => - 'You\'ll be able to add more accounts within the app.'; + String get addMoreAccounts => 'Sie können später weitere Konten hinzufügen.'; @override String get liquidityDescription => - 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + 'Dies dient als Basis für Einnahmen, Ausgaben und Vermögensberechnung.'; @override - String get mainAccount => 'Main account'; + String get mainAccount => 'Hauptkonto'; @override - String get setAmount => 'Set amount'; + String get setAmount => 'Betrag festlegen'; @override - String get editIconAndColor => 'Edit icon and color'; + String get editIconAndColor => 'Symbol und Farbe bearbeiten'; @override - String get skipStepOrStartFromZero => - 'Or you can skip this step and start from 0'; + String get skipStepOrStartFromZero => 'Oder überspringen und bei 0 beginnen'; @override - String get startTrackingExpenses => 'Start tracking your expenses'; + String get startTrackingExpenses => 'Ausgaben tracken'; @override - String get startFromZero => 'Start from 0'; + String get startFromZero => 'Bei 0 beginnen'; @override String get importExport => 'Import/Export'; @override - String get importData => 'Import data'; + String get importData => 'Daten importieren'; @override String get importDataDescription => - 'Import a CSV file to update your database'; + 'CSV-Datei importieren, um Datenbank zu aktualisieren'; @override - String get importMoneyManager => 'Import from Money Manager'; + String get importMoneyManager => 'Von Money Manager importieren'; @override String get importMoneyManagerDescription => - 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + 'CSV von Money Manager importieren (als CSV aus XLS gespeichert).'; @override - String get exportData => 'Export data'; + String get exportData => 'Daten exportieren'; @override - String get exportDataDescription => 'Save your data as a CSV file'; + String get exportDataDescription => 'Daten als CSV-Datei speichern'; @override - String get warningOverwrite => 'Warning: Data Overwrite'; + String get warningOverwrite => 'Warnung: Daten überschreiben'; @override String get warningOverwriteContent => - 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + 'Das Importieren ersetzt alle vorhandenen Daten dauerhaft. Erstellen Sie vorher ein Backup.'; @override - String get proceedImport => 'Proceed with Import'; + String get proceedImport => 'Import fortsetzen'; @override - String get importSuccess => 'Data imported successfully'; + String get importSuccess => 'Daten erfolgreich importiert'; @override String exportFailed(Object err) { - return 'Export failed: $err'; + return 'Export fehlgeschlagen: $err'; } @override String errorExporting(Object tableName) { - return 'Failed to export table: $tableName'; + return 'Fehler beim Exportieren der Tabelle: $tableName'; } @override - String get errorCsvNotFound => 'CSV file not found.'; + String get errorCsvNotFound => 'CSV-Datei nicht gefunden.'; @override - String get errorCsvEmpty => 'The CSV file is empty.'; + String get errorCsvEmpty => 'Die CSV-Datei ist leer.'; @override String errorCsvExpectedColumn(Object column) { - return 'Missing expected column: $column'; + return 'Erwartete Spalte fehlt: $column'; } @override String errorCsvUnexpectedValue(Object value) { - return 'Found an unexpected value: $value'; + return 'Unerwarteter Wert gefunden: $value'; } @override String errorCsvImportGeneral(Object error) { - return 'A general error occurred during CSV import. With error: $error'; + return 'Allgemeiner Fehler beim CSV-Import: $error'; } @override String errorCsvTransactionImport(Object date) { - return 'Failed to import transaction on date: $date'; + return 'Fehler beim Import der Transaktion am: $date'; } @override String errorCleanDatabase(Object error) { - return 'Failed to clean the database. Reason: $error'; + return 'Datenbankbereinigung fehlgeschlagen: $error'; } @override String errorResetDatabase(Object error) { - return 'Failed to reset the database. Reason: $error'; + return 'Datenbank-Reset fehlgeschlagen: $error'; } @override String transactionCount(Object count) { - return '$count transactions'; + return '$count Transaktionen'; } @override - String get uncategorized => 'Uncategorized'; + String get uncategorized => 'Nicht kategorisiert'; @override - String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + String get noIncomesForSelectedMonth => 'Keine Einnahmen für diesen Monat'; @override - String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + String get noExpensesForSelectedMonth => 'Keine Ausgaben für diesen Monat'; @override - String get total => 'Total'; + String get total => 'Gesamt'; @override - String get noTransactionsAdded => 'There are no transactions added yet'; + String get noTransactionsAdded => 'Noch keine Transaktionen hinzugefügt'; @override String get addTransactionCallToAction => - 'Add a transaction to make this section more appealing'; + 'Fügen Sie eine Transaktion hinzu, um diesen Bereich zu füllen'; @override String get graphsEmptyState => - 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + 'Nach dem Hinzufügen von Transaktionen erscheinen hier Grafiken... wie von Zauberhand!'; @override - String get availableLiquidity => 'Available liquidity'; + String get availableLiquidity => 'Verfügbare Liquidität'; @override - String get vsLastMonth => 'VS last month'; + String get vsLastMonth => 'vs. letzter Monat'; @override - String get monthlyBalance => 'Monthly balance'; + String get monthlyBalance => 'Monatssaldo'; @override - String get currentMonth => 'Current month'; + String get currentMonth => 'Aktueller Monat'; @override - String get lastMonth => 'Last month'; + String get lastMonth => 'Letzter Monat'; @override - String get yourAccounts => 'Your accounts'; + String get yourAccounts => 'Ihre Konten'; @override - String get yourBudgets => 'Your budgets'; + String get yourBudgets => 'Ihre Budgets'; @override - String get createBudgetToTrack => 'Create a budget to track your spending'; + String get createBudgetToTrack => + 'Budget erstellen, um Ausgaben zu verfolgen'; @override - String get close => 'Close'; + String get close => 'Schließen'; @override - String get edit => 'Edit'; + String get edit => 'Bearbeiten'; @override - String get errorDuplicatingTransaction => 'Error duplicating transaction'; + String get errorDuplicatingTransaction => + 'Fehler beim Duplizieren der Transaktion'; @override String transactionCreated(Object transaction) { - return '\"$transaction\" has been created'; + return '\"$transaction\" wurde erstellt'; } @override - String get left => 'Left'; + String get left => 'Übrig'; @override String get notEnoughDataForGraph => - 'We are sorry but there is not\nenough data to make the graph...'; + 'Nicht genügend Daten für die Grafik vorhanden...'; @override - String get generalSettingsDesc => 'Edit general settings'; + String get generalSettingsDesc => 'Allgemeine Einstellungen bearbeiten'; @override - String get accountsDesc => 'Add or edit your accounts'; + String get accountsDesc => 'Konten hinzufügen oder bearbeiten'; @override - String get categoriesDesc => 'Add/edit categories and subcategories'; + String get categoriesDesc => 'Kategorien und Unterkategorien verwalten'; @override String get budget => 'Budget'; @override - String get budgetDesc => 'Add or edit your budgets'; + String get budgetDesc => 'Budgets hinzufügen oder bearbeiten'; @override - String get importExportDesc => 'Import or export data'; + String get importExportDesc => 'Daten importieren oder exportieren'; @override - String get notificationsDesc => 'Manage your notifications settings'; + String get notificationsDesc => 'Benachrichtigungen verwalten'; @override - String get leaveFeedback => 'Leave a feedback'; + String get leaveFeedback => 'Feedback geben'; @override - String get leaveFeedbackDesc => - 'Complete a small form to report a bug or leave a feedback'; + String get leaveFeedbackDesc => 'Fehler melden oder Feedback hinterlassen'; @override - String get appInfoDesc => 'Learn more about us and the app'; + String get appInfoDesc => 'Mehr über uns und die App erfahren'; } diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index 460786fd..8fbf7ea7 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -12,169 +12,169 @@ class AppLocalizationsEs extends AppLocalizations { String get appName => 'Sossoldi'; @override - String get dashboard => 'Dashboard'; + String get dashboard => 'Panel'; @override - String get transactions => 'Transactions'; + String get transactions => 'Transacciones'; @override - String get planning => 'Planning'; + String get planning => 'Planificación'; @override - String get graphs => 'Graphs'; + String get graphs => 'Gráficos'; @override - String get list => 'List'; + String get list => 'Lista'; @override - String get categories => 'Categories'; + String get categories => 'Categorías'; @override - String get expenses => 'Expenses'; + String get expenses => 'Gastos'; @override - String get incomes => 'Incomes'; + String get incomes => 'Ingresos'; @override - String get expense => 'Expense'; + String get expense => 'Gasto'; @override - String get income => 'Income'; + String get income => 'Ingreso'; @override - String get transfer => 'Transfer'; + String get transfer => 'Transferencia'; @override - String get accounts => 'Accounts'; + String get accounts => 'Cuentas'; @override - String get details => 'Details'; + String get details => 'Detalles'; @override - String get account => 'Account'; + String get account => 'Cuenta'; @override - String get category => 'Category'; + String get category => 'Categoría'; @override - String get date => 'Date'; + String get date => 'Fecha'; @override - String get investments => 'Investments'; + String get investments => 'Inversiones'; @override - String get settings => 'Settings'; + String get settings => 'Ajustes'; @override - String get notifications => 'Notifications'; + String get notifications => 'Notificaciones'; @override - String get settingsDisclaimer => 'Open source, built by the community'; + String get settingsDisclaimer => 'Open source, desarrollada por la comunidad'; @override - String get addTransaction => 'Add transaction'; + String get addTransaction => 'Añadir transacción'; @override - String get totalBalance => 'Total balance'; + String get totalBalance => 'Saldo Total'; @override - String get netWorth => 'Net worth'; + String get netWorth => 'Patrimonio Neto'; @override - String get save => 'Save'; + String get save => 'Guardar'; @override - String get cancel => 'Cancel'; + String get cancel => 'Cancelar'; @override - String get success => 'Success'; + String get success => 'Éxito'; @override String get ok => 'Ok'; @override - String get editingTransaction => 'Editing transaction'; + String get editingTransaction => 'Editar transacción'; @override - String get newTransaction => 'New transaction'; + String get newTransaction => 'Nueva transacción'; @override - String get updateTransaction => 'Update transaction'; + String get updateTransaction => 'Actualizar transacción'; @override - String get recurringPayments => 'Recurring payments'; + String get recurringPayments => 'Pagos recurrentes'; @override - String get interval => 'Interval'; + String get interval => 'Intervalo'; @override - String get endRepetition => 'End repetition'; + String get endRepetition => 'Fin de la repetición'; @override - String get never => 'Never'; + String get never => 'Nunca'; @override - String get onADate => 'On a date'; + String get onADate => 'En una fecha'; @override - String get switchDisabled => 'Switch is disabled'; + String get switchDisabled => 'Gestor desactivado'; @override String get recurringTransactionWarning => - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + 'Esta es una transacción generada por una recurrente: cualquier cambio afectará solo a esta transacción.\nPara cambiar todas las transacciones futuras u opciones de recurrencia, TOCA AQUÍ.'; @override String saveCsvFileFailed(Object e) { - return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + return 'No es posible guardar archivos aquí, crea o selecciona una carpeta en Descargas o Documentos. Error: $e'; } @override String errorPickingFile(Object error) { - return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + return 'Error al seleccionar el archivo. Asegúrate de tener los permisos necesarios. Error: $error'; } @override String get storagePermissionRequired => - 'Storage permission is required to access your files.'; + 'Se requiere permiso de almacenamiento para acceder a los archivos.'; @override - String get importingData => 'Importing data...'; + String get importingData => 'Importando datos...'; @override - String get exportingData => 'Exporting data...'; + String get exportingData => 'Exportando datos...'; @override String fileSavedTo(Object path) { - return 'File saved to: $path'; + return 'Archivo guardado en: $path'; } @override - String get dataImportedSuccessfully => 'Data imported successfully'; + String get dataImportedSuccessfully => 'Datos importados con éxito'; @override - String get description => 'Description'; + String get description => 'Descripción'; @override - String get addDescription => 'Add description'; + String get addDescription => 'Añadir descripción'; @override - String get duplicateTransactionTitle => 'Duplicate transaction'; + String get duplicateTransactionTitle => 'Duplicar transacción'; @override String get duplicateTransactionContent => - 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + 'Esta transacción ya está en la lista. ¿Deseas duplicarla? Podrás editar la nueva entrada posteriormente.'; @override - String get duplicate => 'Duplicate'; + String get duplicate => 'Duplicar'; @override - String get moreFrequent => 'More frequent'; + String get moreFrequent => 'Más frecuente'; @override - String get allCategories => 'All categories'; + String get allCategories => 'Todas las categorías'; @override - String get allAccounts => 'All accounts'; + String get allAccounts => 'Todas las cuentas'; @override String errorOccurred(Object err) { @@ -182,596 +182,600 @@ class AppLocalizationsEs extends AppLocalizations { } @override - String get selectAccount => 'Select Account'; + String get selectAccount => 'Seleccionar cuenta'; @override - String get to => 'To:'; + String get to => 'Para:'; @override - String get from => 'From:'; + String get from => 'De:'; @override - String get recurringTransactionAdded => 'Recurring transaction added'; + String get recurringTransactionAdded => 'Transacción recurrente añadida'; @override - String get recurringTransactions => 'Recurring transactions'; + String get recurringTransactions => 'Transacciones recurrentes'; @override - String get addTransactionReminder => 'Add transaction reminder'; + String get addTransactionReminder => 'Añadir recordatorio de transacción'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Política de Privacidad'; @override - String get privacyCollectTitle => 'What Information Do We Collect?'; + String get privacyCollectTitle => '¿Qué información recopilamos?'; @override - String get privacyChangesTitle => 'Changes to This Privacy Policy'; + String get privacyChangesTitle => 'Cambios en la Política de Privacidad'; @override - String get contactUsTitle => 'Contact us'; + String get contactUsTitle => 'Contáctanos'; @override String get privacyIntro => - 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + 'Sossoldi está desarrollado como una aplicación open source. Este servicio se proporciona de forma gratuita y está destinado a ser usado tal cual.\nNo tenemos interés en recopilar ninguna información personal. Creemos que dicha información te pertenece solo a ti. No almacenamos ni transmitimos tus datos personales, ni incluimos software de publicidad o análisis que se comunique con terceros.\n'; @override String get privacyCollectBody => - 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + 'Sossoldi no recopila ninguna información personal ni se conecta a Internet. Cualquier información añadida a la aplicación existe exclusivamente en tu dispositivo y en ningún otro lugar.\n'; @override String get privacyChangesBody => - 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + 'Podemos actualizar nuestra Política de Privacidad ocasionalmente. Por tanto, recomendamos que revises esta página periódicamente para verificar cambios.\nEsta política entra en vigor a partir del 01/01/2024.\n'; @override String get contactUsBody => - 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + 'Si tienes dudas o sugerencias sobre nuestra Política de Privacidad, no dudes en contactarnos en\n'; @override - String get collaboratorsTitle => 'Collaborators'; + String get collaboratorsTitle => 'Colaboradores'; @override - String get meetTheTeam => 'Meet the team'; + String get meetTheTeam => 'Conoce al equipo'; @override String get teamDescription => - 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + 'Sossoldi es desarrollada y mantenida por una apasionada comunidad open source. Cada funcionalidad, corrección e idea viene de personas como tú.'; @override - String get wantToContribute => 'Want to contribute?'; + String get wantToContribute => '¿Quieres contribuir?'; @override String get contributeDescription => - 'Open an issue, submit a PR or just say hi on GitHub'; + 'Abre un issue, envía un PR o simplemente saluda en GitHub'; @override - String get appInfo => 'App Info'; + String get appInfo => 'Información de la app'; @override - String get appVersion => 'App Version:'; + String get appVersion => 'Versión de la app:'; @override - String get collaborators => 'Collaborators'; + String get collaborators => 'Colaboradores'; @override - String get collaboratorsDescription => 'See the team behind this app'; + String get collaboratorsDescription => 'Conoce al equipo detrás de esta app'; @override - String get privacyPolicy => 'Privacy Policy'; + String get privacyPolicy => 'Política de Privacidad'; @override - String get privacyPolicyDescription => 'Read more'; + String get privacyPolicyDescription => 'Saber más'; @override - String get generalSettings => 'General Settings'; + String get generalSettings => 'Ajustes generales'; @override - String get appearance => 'Appearance'; + String get appearance => 'Apariencia'; @override - String get currency => 'Currency'; + String get currency => 'Moneda'; @override - String get requireAuthentication => 'Require authentication'; + String get requireAuthentication => 'Requerir autenticación'; @override - String get searchForATransaction => 'Search for a transaction'; + String get searchForATransaction => 'Buscar una transacción'; @override - String get selectACurrency => 'Select a currency'; + String get selectACurrency => 'Seleccionar una moneda'; @override - String get search => 'Search'; + String get search => 'Buscar'; @override - String get searchIn => 'Search in'; + String get searchIn => 'Buscar en'; @override - String get lastTransactions => 'Your last transactions'; + String get lastTransactions => 'Tus últimas transacciones'; @override - String get startReconciliation => 'Start reconciliation'; + String get startReconciliation => 'Iniciar conciliación'; @override - String get newBalance => 'New balance'; + String get newBalance => 'Nuevo saldo'; @override - String get balanceDiscrepancy => 'Balance Discrepancy?'; + String get balanceDiscrepancy => '¿Diferencia de saldo?'; @override String get balanceAdjustmentHint => - 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + 'El saldo registrado puede diferir del extracto bancario. Toca abajo para ajustar el saldo manualmente y mantener tus registros actualizados.'; @override - String get newAccount => 'New account'; + String get newAccount => 'Nueva cuenta'; @override - String get editAccount => 'Edit account'; + String get editAccount => 'Editar cuenta'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Crear cuenta'; @override - String get accountName => 'Account name'; + String get accountName => 'Nombre de la cuenta'; @override - String get name => 'Name'; + String get name => 'Nombre'; @override - String get iconAndColor => 'Icon and color'; + String get iconAndColor => 'Icono y color'; @override - String get chooseColor => 'Choose color'; + String get chooseColor => 'Elegir color'; @override - String get chooseIcon => 'Choose icon'; + String get chooseIcon => 'Elegir icono'; @override - String get done => 'Fatto'; + String get done => 'Hecho'; @override - String get add => 'Add'; + String get add => 'Añadir'; @override - String get setAsMainAccount => 'Set as main account'; + String get setAsMainAccount => 'Definir como cuenta principal'; @override - String get countsForNetWorth => 'Counts for the net worth'; + String get countsForNetWorth => 'Incluir en el patrimonio neto'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Eliminar cuenta'; @override - String get initialBalance => 'Initial balance'; + String get initialBalance => 'Saldo inicial'; @override - String get currentBalance => 'Current balance'; + String get currentBalance => 'Saldo actual'; @override - String get showLess => 'Show less'; + String get showLess => 'Mostrar menos'; @override - String get showMore => 'Show more'; + String get showMore => 'Mostrar más'; @override - String get addSubcategory => 'Add subcategory'; + String get addSubcategory => 'Añadir subcategoría'; @override - String get newCategory => 'New category'; + String get newCategory => 'Nueva categoría'; @override - String get editCategory => 'Edit category'; + String get editCategory => 'Editar categoría'; @override - String get createCategory => 'Create category'; + String get createCategory => 'Crear categoría'; @override - String get updateCategory => 'Update category'; + String get updateCategory => 'Actualizar categoría'; @override - String get categoryName => 'Category name'; + String get categoryName => 'Nombre de la categoría'; @override - String get type => 'Type'; + String get type => 'Tipo'; @override - String get deleteCategory => 'Delete category'; + String get deleteCategory => 'Eliminar categoría'; @override - String get newSubcategory => 'New subcategory'; + String get newSubcategory => 'Nueva subcategoría'; @override - String get editSubcategory => 'Edit subcategory'; + String get editSubcategory => 'Editar subcategoría'; @override - String get createSubcategory => 'Create subcategory'; + String get createSubcategory => 'Crear subcategoría'; @override - String get updateSubcategory => 'Update subcategory'; + String get updateSubcategory => 'Actualizar subcategoría'; @override - String get subcategoryName => 'Subcategory name'; + String get subcategoryName => 'Nombre de la subcategoría'; @override - String get deleteSubcategory => 'Delete subcategory'; + String get deleteSubcategory => 'Eliminar subcategoría'; @override - String get subcategory => 'Subcategory'; + String get subcategory => 'Subcategoría'; @override - String get categoryFirstThenBudget => 'Add a category first to set a budget'; + String get categoryFirstThenBudget => + 'Añade una categoría antes de crear un presupuesto'; @override String inTheNextDays(Object next) { - return 'In $next days'; + return 'En los próximos $next días'; } @override - String get monthlyBudget => 'Monthly budget'; + String get monthlyBudget => 'Presupuesto mensual'; @override - String get manage => 'Manage'; + String get manage => 'Gestionar'; @override - String get swipeLeftToDelete => 'Swipe left to delete'; + String get swipeLeftToDelete => 'Desliza a la izquierda para eliminar'; @override - String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + String get yourMonthlyBudgetWillBe => 'Tu presupuesto mensual será:'; @override - String get saveBudget => 'Save budget'; + String get saveBudget => 'Guardar presupuesto'; @override String get selectCategoriesToCreateBudget => - 'Select the categories to create your budget'; + 'Selecciona las categorías para crear tu presupuesto'; @override - String get amount => 'Amount'; + String get amount => 'Valor'; @override - String get addCategoryBudget => 'Add category budget'; + String get addCategoryBudget => 'Añadir presupuesto a la categoría'; @override String get allCategoriesAdded => - 'You have already added all available categories.'; + 'Ya has añadido todas las categorías disponibles.'; @override - String get delete => 'Delete'; + String get delete => 'Eliminar'; @override String get allRecurringPaymentsHere => - 'All recurring payments will be displayed here'; + 'Todos los pagos recurrentes se mostrarán aquí'; @override - String get addRecurringPayment => 'Add recurring payment'; + String get addRecurringPayment => 'Añadir pago recurrente'; @override - String get seeOlderPayments => 'See older payments'; + String get seeOlderPayments => 'Ver pagos antiguos'; @override String untilDate(Object date) { - return 'Until $date'; + return 'Hasta $date'; } @override - String get olderPayments => 'Older payments'; + String get olderPayments => 'Pagos antiguos'; @override - String get categoryNotFound => 'Category not found'; + String get categoryNotFound => 'Categoría no encontrada'; @override - String get back => 'Back'; + String get back => 'Volver'; @override String onTheDay(Object day) { - return '- On the $day day'; + return '- El día $day'; } @override - String get noMonthlyPaymentHistory => 'No monthly payment history'; + String get noMonthlyPaymentHistory => 'Sin historial de pagos mensuales'; @override - String get noRecurrentPaymentHistory => 'No recurrent payment history'; + String get noRecurrentPaymentHistory => 'Sin historial de pagos recurrentes'; @override String errorLoadingPayments(Object error) { - return 'Error loading payments: $error'; + return 'Error al cargar pagos: $error'; } @override - String get editRecurringTransaction => 'Edit recurring transaction'; + String get editRecurringTransaction => 'Editar transacción recurrente'; @override String get detailsExplanation => - 'Details (any change will affect only future transactions)'; + 'Detalles (cualquier cambio afectará solo a transacciones futuras)'; @override - String get dateStart => 'Date start'; + String get dateStart => 'Fecha de inicio'; @override - String get planned => 'Planned'; + String get planned => 'Planeado'; @override - String get composition => 'Composition'; + String get composition => 'Composición'; @override - String get progress => 'Progress'; + String get progress => 'Progreso'; @override - String get noBudgetSet => 'There are no budgets set'; + String get noBudgetSet => 'Ningún presupuesto definido'; @override String get budgetHelpText => - 'A monthly budget can help you keep track of your expenses and stay within the limits'; + 'Un presupuesto mensual puede ayudarte a realizar un seguimiento de tus gastos y mantenerte dentro de los límites'; @override - String get createBudget => 'Create budget'; + String get createBudget => 'Crear presupuesto'; @override - String get setUpTheApp => 'Set up the app'; + String get setUpTheApp => 'Configurar la app'; @override String get setupDescription => - 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + 'En algunos pasos estarás listo para empezar a\nrealizar un seguimiento de tus finanzas personales (casi)\ncomo Mr. Rip.'; @override - String get startTheSetup => 'Start the setup'; + String get startTheSetup => 'Iniciar configuración'; @override String budgetAmount(Object amount) { - return 'Budget $amount€'; + return 'Presupuesto $amount€'; } @override - String get addBudget => 'Add budget'; + String get addBudget => 'Añadir presupuesto'; @override String addBudgetForCategory(Object cat) { - return 'Add budget for category $cat'; + return 'Añadir presupuesto para la categoría $cat'; } @override - String get addCategory => 'Add category'; + String get addCategory => 'Añadir categoría'; @override - String get confirm => 'Confirm'; + String get confirm => 'Confirmar'; @override - String get step1Of2 => 'Step 1 of 2'; + String get step1Of2 => 'Paso 1 de 2'; @override - String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + String get setupMonthlyBudgets => 'Define tus presupuestos\nmensuales'; @override String get chooseCategoriesForBudget => - 'Choose which categories you want to set a budget for'; + 'Elige las categorías para las que deseas definir un presupuesto'; @override - String get monthlyBudgetTotal => 'Monthly budget total:'; + String get monthlyBudgetTotal => 'Total del presupuesto mensual:'; @override - String get nextStep => 'Next step'; + String get nextStep => 'Próximo paso'; @override - String get continueWithoutBudget => 'Continue without budget'; + String get continueWithoutBudget => 'Continuar sin presupuesto'; @override - String get step2Of2 => 'Step 2 OF 2'; + String get step2Of2 => 'Paso 2 de 2'; @override String get setLiquidityInMainAccount => - 'Set the liquidity in your main account'; + 'Define la liquidez en tu cuenta principal'; @override - String get addMoreAccounts => - 'You\'ll be able to add more accounts within the app.'; + String get addMoreAccounts => 'Podrás añadir más cuentas dentro de la app'; @override String get liquidityDescription => - 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + 'Se usará como base para añadir ingresos, gastos y calcular tu patrimonio.\nPodrás añadir más cuentas dentro de la app.'; @override - String get mainAccount => 'Main account'; + String get mainAccount => 'Cuenta principal'; @override - String get setAmount => 'Set amount'; + String get setAmount => 'Definir valor'; @override - String get editIconAndColor => 'Edit icon and color'; + String get editIconAndColor => 'Editar icono y color'; @override String get skipStepOrStartFromZero => - 'Or you can skip this step and start from 0'; + 'O puedes saltar este paso y empezar desde 0'; @override - String get startTrackingExpenses => 'Start tracking your expenses'; + String get startTrackingExpenses => + 'Empezar a realizar un seguimiento de tus gastos'; @override - String get startFromZero => 'Start from 0'; + String get startFromZero => 'Empezar desde 0'; @override - String get importExport => 'Import/Export'; + String get importExport => 'Importar/Exportar'; @override - String get importData => 'Import data'; + String get importData => 'Importar datos'; @override String get importDataDescription => - 'Import a CSV file to update your database'; + 'Importa un archivo CSV para actualizar la base de datos'; @override - String get importMoneyManager => 'Import from Money Manager'; + String get importMoneyManager => 'Importar de Money Manager'; @override String get importMoneyManagerDescription => - 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + 'Importa CSV de Money Manager para actualizar la base de datos. El archivo debe guardarse en formato CSV desde XLS.'; @override - String get exportData => 'Export data'; + String get exportData => 'Exportar datos'; @override - String get exportDataDescription => 'Save your data as a CSV file'; + String get exportDataDescription => 'Guarda tus datos como un archivo CSV'; @override - String get warningOverwrite => 'Warning: Data Overwrite'; + String get warningOverwrite => 'Atención: Sobrescritura de datos'; @override String get warningOverwriteContent => - 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + 'La importación de este archivo reemplazará permanentemente tus datos existentes. Esta acción no se puede deshacer. Asegúrate de tener una copia de seguridad antes de proceder.'; @override - String get proceedImport => 'Proceed with Import'; + String get proceedImport => 'Proceder con la importación'; @override - String get importSuccess => 'Data imported successfully'; + String get importSuccess => 'Datos importados con éxito'; @override String exportFailed(Object err) { - return 'Export failed: $err'; + return 'Fallo en la exportación: $err'; } @override String errorExporting(Object tableName) { - return 'Failed to export table: $tableName'; + return 'No fue posible exportar la tabla: $tableName'; } @override - String get errorCsvNotFound => 'CSV file not found.'; + String get errorCsvNotFound => 'Archivo CSV no encontrado.'; @override - String get errorCsvEmpty => 'The CSV file is empty.'; + String get errorCsvEmpty => 'El archivo CSV está vacío.'; @override String errorCsvExpectedColumn(Object column) { - return 'Missing expected column: $column'; + return 'Columna faltando en el CSV: $column'; } @override String errorCsvUnexpectedValue(Object value) { - return 'Found an unexpected value: $value'; + return 'Valor inesperado encontrado: $value'; } @override String errorCsvImportGeneral(Object error) { - return 'A general error occurred during CSV import. With error: $error'; + return 'Ocurrió un error general durante la importación del CSV. Error: $error'; } @override String errorCsvTransactionImport(Object date) { - return 'Failed to import transaction on date: $date'; + return 'Error al importar la transacción en la fecha: $date'; } @override String errorCleanDatabase(Object error) { - return 'Failed to clean the database. Reason: $error'; + return 'No fue posible limpiar la base de datos. Motivo: $error'; } @override String errorResetDatabase(Object error) { - return 'Failed to reset the database. Reason: $error'; + return 'No fue posible restablecer la base de datos. Motivo: $error'; } @override String transactionCount(Object count) { - return '$count transactions'; + return '$count transacciones'; } @override - String get uncategorized => 'Uncategorized'; + String get uncategorized => 'Sin categoría'; @override - String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + String get noIncomesForSelectedMonth => + 'Ningún ingreso para el mes seleccionado'; @override - String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + String get noExpensesForSelectedMonth => + 'Ningún gasto para el mes seleccionado'; @override String get total => 'Total'; @override - String get noTransactionsAdded => 'There are no transactions added yet'; + String get noTransactionsAdded => 'Ninguna transacción añadida aún'; @override String get addTransactionCallToAction => - 'Add a transaction to make this section more appealing'; + 'Añade una transacción para hacer esta sección más interesante'; @override String get graphsEmptyState => - 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + 'Después de añadir algunas transacciones, gráficos increíbles aparecerán aquí... ¡casi como por arte de magia!'; @override - String get availableLiquidity => 'Available liquidity'; + String get availableLiquidity => 'Liquidez disponible'; @override - String get vsLastMonth => 'VS last month'; + String get vsLastMonth => 'VS mes anterior'; @override - String get monthlyBalance => 'Monthly balance'; + String get monthlyBalance => 'Saldo mensual'; @override - String get currentMonth => 'Current month'; + String get currentMonth => 'Mes actual'; @override - String get lastMonth => 'Last month'; + String get lastMonth => 'Mes anterior'; @override - String get yourAccounts => 'Your accounts'; + String get yourAccounts => 'Tus cuentas'; @override - String get yourBudgets => 'Your budgets'; + String get yourBudgets => 'Tus presupuestos'; @override - String get createBudgetToTrack => 'Create a budget to track your spending'; + String get createBudgetToTrack => + 'Crea un presupuesto para realizar un seguimiento de tus gastos'; @override - String get close => 'Close'; + String get close => 'Cerrar'; @override - String get edit => 'Edit'; + String get edit => 'Editar'; @override - String get errorDuplicatingTransaction => 'Error duplicating transaction'; + String get errorDuplicatingTransaction => 'Error al duplicar la transacción'; @override String transactionCreated(Object transaction) { - return '\"$transaction\" has been created'; + return '\"$transaction\" fue creada'; } @override - String get left => 'Left'; + String get left => 'Restante'; @override String get notEnoughDataForGraph => - 'We are sorry but there is not\nenough data to make the graph...'; + 'Lamentamos que no haya\nsuficientes datos para crear el gráfico...'; @override - String get generalSettingsDesc => 'Edit general settings'; + String get generalSettingsDesc => 'Editar ajustes generales'; @override - String get accountsDesc => 'Add or edit your accounts'; + String get accountsDesc => 'Añadir o editar tus cuentas'; @override - String get categoriesDesc => 'Add/edit categories and subcategories'; + String get categoriesDesc => 'Añadir/editar categorías y subcategorías'; @override - String get budget => 'Budget'; + String get budget => 'Presupuesto'; @override - String get budgetDesc => 'Add or edit your budgets'; + String get budgetDesc => 'Añadir o editar tus presupuestos'; @override - String get importExportDesc => 'Import or export data'; + String get importExportDesc => 'Importar o exportar datos'; @override - String get notificationsDesc => 'Manage your notifications settings'; + String get notificationsDesc => 'Gestionar tus ajustes de notificación'; @override - String get leaveFeedback => 'Leave a feedback'; + String get leaveFeedback => 'Dejar comentarios'; @override String get leaveFeedbackDesc => - 'Complete a small form to report a bug or leave a feedback'; + 'Completa un pequeño formulario para reportar un error o dejar comentarios'; @override - String get appInfoDesc => 'Learn more about us and the app'; + String get appInfoDesc => 'Saber más sobre nosotros y la app'; } diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index 57d5edee..8c453fea 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -15,507 +15,510 @@ class AppLocalizationsNl extends AppLocalizations { String get dashboard => 'Dashboard'; @override - String get transactions => 'Transactions'; + String get transactions => 'Transacties'; @override String get planning => 'Planning'; @override - String get graphs => 'Graphs'; + String get graphs => 'Grafieken'; @override - String get list => 'List'; + String get list => 'Lijst'; @override - String get categories => 'Categories'; + String get categories => 'Categorieën'; @override - String get expenses => 'Expenses'; + String get expenses => 'Uitgaven'; @override - String get incomes => 'Incomes'; + String get incomes => 'Inkomsten'; @override - String get expense => 'Expense'; + String get expense => 'Uitgave'; @override - String get income => 'Income'; + String get income => 'Inkomst'; @override - String get transfer => 'Transfer'; + String get transfer => 'Overboeking'; @override - String get accounts => 'Accounts'; + String get accounts => 'Rekeningen'; @override String get details => 'Details'; @override - String get account => 'Account'; + String get account => 'Rekening'; @override - String get category => 'Category'; + String get category => 'Categorie'; @override - String get date => 'Date'; + String get date => 'Datum'; @override - String get investments => 'Investments'; + String get investments => 'Investeringen'; @override - String get settings => 'Settings'; + String get settings => 'Instellingen'; @override - String get notifications => 'Notifications'; + String get notifications => 'Meldingen'; @override - String get settingsDisclaimer => 'Open source, built by the community'; + String get settingsDisclaimer => 'Open source, gebouwd door de community'; @override - String get addTransaction => 'Add transaction'; + String get addTransaction => 'Transactie toevoegen'; @override - String get totalBalance => 'Total balance'; + String get totalBalance => 'Totaal saldo'; @override - String get netWorth => 'Net worth'; + String get netWorth => 'Nettowaarde'; @override - String get save => 'Save'; + String get save => 'Opslaan'; @override - String get cancel => 'Cancel'; + String get cancel => 'Annuleren'; @override - String get success => 'Success'; + String get success => 'Succes'; @override String get ok => 'Ok'; @override - String get editingTransaction => 'Editing transaction'; + String get editingTransaction => 'Transactie bewerken'; @override - String get newTransaction => 'New transaction'; + String get newTransaction => 'Nieuwe transactie'; @override - String get updateTransaction => 'Update transaction'; + String get updateTransaction => 'Transactie bijwerken'; @override - String get recurringPayments => 'Recurring payments'; + String get recurringPayments => 'Terugkerende betalingen'; @override String get interval => 'Interval'; @override - String get endRepetition => 'End repetition'; + String get endRepetition => 'Einde herhaling'; @override - String get never => 'Never'; + String get never => 'Nooit'; @override - String get onADate => 'On a date'; + String get onADate => 'Op een datum'; @override - String get switchDisabled => 'Switch is disabled'; + String get switchDisabled => 'Schakelaar is uitgeschakeld'; @override String get recurringTransactionWarning => - 'This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.'; + 'Dit is een transactie gegenereerd door een terugkerende: elke wijziging heeft alleen invloed op deze unieke transactie.\nOm alle toekomstige transacties of herhalingsopties te wijzigen, TIK HIER.'; @override String saveCsvFileFailed(Object e) { - return 'Cannot save the file here, please create or select a folder in Downloads or Documents. Error: $e'; + return 'Kan het bestand hier niet opslaan, kies een map in Downloads of Documenten. Fout: $e'; } @override String errorPickingFile(Object error) { - return 'Error picking file. Please ensure you have sufficient permissions. Error: $error'; + return 'Fout bij selecteren bestand. Controleer of je voldoende rechten hebt. Fout: $error'; } @override String get storagePermissionRequired => - 'Storage permission is required to access your files.'; + 'Opslagtoegang is vereist om je bestanden te openen.'; @override - String get importingData => 'Importing data...'; + String get importingData => 'Gegevens importeren...'; @override - String get exportingData => 'Exporting data...'; + String get exportingData => 'Gegevens exporteren...'; @override String fileSavedTo(Object path) { - return 'File saved to: $path'; + return 'Bestand opgeslagen in: $path'; } @override - String get dataImportedSuccessfully => 'Data imported successfully'; + String get dataImportedSuccessfully => 'Gegevens succesvol geïmporteerd'; @override - String get description => 'Description'; + String get description => 'Beschrijving'; @override - String get addDescription => 'Add description'; + String get addDescription => 'Beschrijving toevoegen'; @override - String get duplicateTransactionTitle => 'Duplicate transaction'; + String get duplicateTransactionTitle => 'Transactie dupliceren'; @override String get duplicateTransactionContent => - 'This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.'; + 'Deze transactie staat al in de lijst. Wil je deze dupliceren? Je kunt de nieuwe transactie daarna bewerken.'; @override - String get duplicate => 'Duplicate'; + String get duplicate => 'Dupliceren'; @override - String get moreFrequent => 'More frequent'; + String get moreFrequent => 'Vaker'; @override - String get allCategories => 'All categories'; + String get allCategories => 'Alle categorieën'; @override - String get allAccounts => 'All accounts'; + String get allAccounts => 'Alle rekeningen'; @override String errorOccurred(Object err) { - return 'Error: $err'; + return 'Fout: $err'; } @override - String get selectAccount => 'Select Account'; + String get selectAccount => 'Selecteer rekening'; @override - String get to => 'To:'; + String get to => 'Naar:'; @override - String get from => 'From:'; + String get from => 'Van:'; @override - String get recurringTransactionAdded => 'Recurring transaction added'; + String get recurringTransactionAdded => 'Terugkerende transactie toegevoegd'; @override - String get recurringTransactions => 'Recurring transactions'; + String get recurringTransactions => 'Terugkerende transacties'; @override - String get addTransactionReminder => 'Add transaction reminder'; + String get addTransactionReminder => 'Herinnering transactie toevoegen'; @override - String get privacyPolicyTitle => 'Privacy Policy'; + String get privacyPolicyTitle => 'Privacybeleid'; @override - String get privacyCollectTitle => 'What Information Do We Collect?'; + String get privacyCollectTitle => 'Welke informatie verzamelen we?'; @override - String get privacyChangesTitle => 'Changes to This Privacy Policy'; + String get privacyChangesTitle => 'Wijzigingen in dit privacybeleid'; @override - String get contactUsTitle => 'Contact us'; + String get contactUsTitle => 'Contact'; @override String get privacyIntro => - 'Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n'; + 'Sossoldi is gebouwd als een open source app. Deze service wordt kosteloos aangeboden en is bedoeld voor gebruik zoals het is.\nWe zijn niet geïnteresseerd in het verzamelen van persoonlijke informatie. Wij geloven dat deze informatie van jou is en van jou alleen. We slaan je persoonlijke gegevens niet op, verzenden ze niet en gebruiken geen advertentie- of analyse-software van derden.\n'; @override String get privacyCollectBody => - 'Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n'; + 'Sossoldi verzamelt geen persoonlijke informatie en maakt geen verbinding met het internet. Alle informatie die je toevoegt, blijft uitsluitend op je apparaat.\n'; @override String get privacyChangesBody => - 'We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n'; + 'We kunnen ons privacybeleid van tijd tot tijd bijwerken. We raden je aan deze pagina regelmatig te controleren op wijzigingen.\nDit beleid is effectief vanaf 2024-01-01.\n'; @override String get contactUsBody => - 'If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n'; + 'Als je vragen of suggesties hebt over ons privacybeleid, neem dan contact met ons op via \n'; @override - String get collaboratorsTitle => 'Collaborators'; + String get collaboratorsTitle => 'Medewerkers'; @override - String get meetTheTeam => 'Meet the team'; + String get meetTheTeam => 'Ontmoet het team'; @override String get teamDescription => - 'Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.'; + 'Sossoldi wordt gebouwd en onderhouden door een gepassioneerde open source community. Elke functie en fix komt van mensen zoals jij.'; @override - String get wantToContribute => 'Want to contribute?'; + String get wantToContribute => 'Wil je bijdragen?'; @override String get contributeDescription => - 'Open an issue, submit a PR or just say hi on GitHub'; + 'Open een issue, stuur een PR of zeg hallo op GitHub'; @override String get appInfo => 'App Info'; @override - String get appVersion => 'App Version:'; + String get appVersion => 'App Versie:'; @override - String get collaborators => 'Collaborators'; + String get collaborators => 'Medewerkers'; @override - String get collaboratorsDescription => 'See the team behind this app'; + String get collaboratorsDescription => 'Zie het team achter deze app'; @override - String get privacyPolicy => 'Privacy Policy'; + String get privacyPolicy => 'Privacybeleid'; @override - String get privacyPolicyDescription => 'Read more'; + String get privacyPolicyDescription => 'Lees meer'; @override - String get generalSettings => 'General Settings'; + String get generalSettings => 'Algemene instellingen'; @override - String get appearance => 'Appearance'; + String get appearance => 'Uiterlijk'; @override - String get currency => 'Currency'; + String get currency => 'Valuta'; @override - String get requireAuthentication => 'Require authentication'; + String get requireAuthentication => 'Authenticatie vereist'; @override - String get searchForATransaction => 'Search for a transaction'; + String get searchForATransaction => 'Zoek naar een transactie'; @override - String get selectACurrency => 'Select a currency'; + String get selectACurrency => 'Selecteer een valuta'; @override - String get search => 'Search'; + String get search => 'Zoeken'; @override - String get searchIn => 'Search in'; + String get searchIn => 'Zoeken in'; @override - String get lastTransactions => 'Your last transactions'; + String get lastTransactions => 'Je laatste transacties'; @override - String get startReconciliation => 'Start reconciliation'; + String get startReconciliation => 'Start aansluiting'; @override - String get newBalance => 'New balance'; + String get newBalance => 'Nieuw saldo'; @override - String get balanceDiscrepancy => 'Balance Discrepancy?'; + String get balanceDiscrepancy => 'Saldo afwijking?'; @override String get balanceAdjustmentHint => - 'Your recorded balance might differ from your bank\'s statement. Tap below to manually adjust your balance and keep your records accurate.'; + 'Je geregistreerde saldo kan afwijken van je bankafschrift. Tik hieronder om je saldo handmatig aan te passen.'; @override - String get newAccount => 'New account'; + String get newAccount => 'Nieuwe rekening'; @override - String get editAccount => 'Edit account'; + String get editAccount => 'Rekening bewerken'; @override - String get createAccount => 'Create account'; + String get createAccount => 'Rekening maken'; @override - String get accountName => 'Account name'; + String get accountName => 'Rekeningnaam'; @override - String get name => 'Name'; + String get name => 'Naam'; @override - String get iconAndColor => 'Icon and color'; + String get iconAndColor => 'Icoon en kleur'; @override - String get chooseColor => 'Choose color'; + String get chooseColor => 'Kies kleur'; @override - String get chooseIcon => 'Choose icon'; + String get chooseIcon => 'Kies icoon'; @override - String get done => 'Fatto'; + String get done => 'Gereed'; @override - String get add => 'Add'; + String get add => 'Toevoegen'; @override - String get setAsMainAccount => 'Set as main account'; + String get setAsMainAccount => 'Instellen als hoofdrekening'; @override - String get countsForNetWorth => 'Counts for the net worth'; + String get countsForNetWorth => 'Telt mee voor nettowaarde'; @override - String get deleteAccount => 'Delete account'; + String get deleteAccount => 'Rekening verwijderen'; @override - String get initialBalance => 'Initial balance'; + String get initialBalance => 'Beginsaldo'; @override - String get currentBalance => 'Current balance'; + String get currentBalance => 'Huidig saldo'; @override - String get showLess => 'Show less'; + String get showLess => 'Minder weergeven'; @override - String get showMore => 'Show more'; + String get showMore => 'Meer weergeven'; @override - String get addSubcategory => 'Add subcategory'; + String get addSubcategory => 'Subcategorie toevoegen'; @override - String get newCategory => 'New category'; + String get newCategory => 'Nieuwe categorie'; @override - String get editCategory => 'Edit category'; + String get editCategory => 'Categorie bewerken'; @override - String get createCategory => 'Create category'; + String get createCategory => 'Categorie maken'; @override - String get updateCategory => 'Update category'; + String get updateCategory => 'Categorie bijwerken'; @override - String get categoryName => 'Category name'; + String get categoryName => 'Categorienaam'; @override String get type => 'Type'; @override - String get deleteCategory => 'Delete category'; + String get deleteCategory => 'Categorie verwijderen'; @override - String get newSubcategory => 'New subcategory'; + String get newSubcategory => 'Nieuwe subcategorie'; @override - String get editSubcategory => 'Edit subcategory'; + String get editSubcategory => 'Subcategorie bewerken'; @override - String get createSubcategory => 'Create subcategory'; + String get createSubcategory => 'Subcategorie maken'; @override - String get updateSubcategory => 'Update subcategory'; + String get updateSubcategory => 'Subcategorie bijwerken'; @override - String get subcategoryName => 'Subcategory name'; + String get subcategoryName => 'Naam subcategorie'; @override - String get deleteSubcategory => 'Delete subcategory'; + String get deleteSubcategory => 'Subcategorie verwijderen'; @override - String get subcategory => 'Subcategory'; + String get subcategory => 'Subcategorie'; @override - String get categoryFirstThenBudget => 'Add a category first to set a budget'; + String get categoryFirstThenBudget => + 'Voeg eerst een categorie toe om een budget in te stellen'; @override String inTheNextDays(Object next) { - return 'In $next days'; + return 'Over $next dagen'; } @override - String get monthlyBudget => 'Monthly budget'; + String get monthlyBudget => 'Maandelijks budget'; @override - String get manage => 'Manage'; + String get manage => 'Beheren'; @override - String get swipeLeftToDelete => 'Swipe left to delete'; + String get swipeLeftToDelete => 'Veeg naar links om te verwijderen'; @override - String get yourMonthlyBudgetWillBe => 'Your monthly budget will be:'; + String get yourMonthlyBudgetWillBe => 'Je maandelijkse budget wordt:'; @override - String get saveBudget => 'Save budget'; + String get saveBudget => 'Budget opslaan'; @override String get selectCategoriesToCreateBudget => - 'Select the categories to create your budget'; + 'Selecteer categorieën voor je budget'; @override - String get amount => 'Amount'; + String get amount => 'Bedrag'; @override - String get addCategoryBudget => 'Add category budget'; + String get addCategoryBudget => 'Categoriebudget toevoegen'; @override String get allCategoriesAdded => - 'You have already added all available categories.'; + 'Je hebt alle beschikbare categorieën al toegevoegd.'; @override - String get delete => 'Delete'; + String get delete => 'Verwijderen'; @override String get allRecurringPaymentsHere => - 'All recurring payments will be displayed here'; + 'Alle terugkerende betalingen worden hier getoond'; @override - String get addRecurringPayment => 'Add recurring payment'; + String get addRecurringPayment => 'Terugkerende betaling toevoegen'; @override - String get seeOlderPayments => 'See older payments'; + String get seeOlderPayments => 'Bekijk oudere betalingen'; @override String untilDate(Object date) { - return 'Until $date'; + return 'Tot $date'; } @override - String get olderPayments => 'Older payments'; + String get olderPayments => 'Oudere betalingen'; @override - String get categoryNotFound => 'Category not found'; + String get categoryNotFound => 'Categorie niet gevonden'; @override - String get back => 'Back'; + String get back => 'Terug'; @override String onTheDay(Object day) { - return '- On the $day day'; + return '- Op de ${day}e dag'; } @override - String get noMonthlyPaymentHistory => 'No monthly payment history'; + String get noMonthlyPaymentHistory => + 'Geen maandelijkse betalingsgeschiedenis'; @override - String get noRecurrentPaymentHistory => 'No recurrent payment history'; + String get noRecurrentPaymentHistory => + 'Geen terugkerende betalingsgeschiedenis'; @override String errorLoadingPayments(Object error) { - return 'Error loading payments: $error'; + return 'Fout bij laden betalingen: $error'; } @override - String get editRecurringTransaction => 'Edit recurring transaction'; + String get editRecurringTransaction => 'Terugkerende transactie bewerken'; @override String get detailsExplanation => - 'Details (any change will affect only future transactions)'; + 'Details (wijzigingen gelden alleen voor toekomstige transacties)'; @override - String get dateStart => 'Date start'; + String get dateStart => 'Startdatum'; @override - String get planned => 'Planned'; + String get planned => 'Gepland'; @override - String get composition => 'Composition'; + String get composition => 'Samenstelling'; @override - String get progress => 'Progress'; + String get progress => 'Voortgang'; @override - String get noBudgetSet => 'There are no budgets set'; + String get noBudgetSet => 'Geen budgetten ingesteld'; @override String get budgetHelpText => - 'A monthly budget can help you keep track of your expenses and stay within the limits'; + 'Een budget helpt je om je uitgaven onder controle te houden'; @override - String get createBudget => 'Create budget'; + String get createBudget => 'Budget maken'; @override - String get setUpTheApp => 'Set up the app'; + String get setUpTheApp => 'App instellen'; @override String get setupDescription => - 'In a few steps you\'ll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.'; + 'In een paar stappen ben je klaar om je financiën bij te houden (bijna) zoals Mr. Rip.'; @override - String get startTheSetup => 'Start the setup'; + String get startTheSetup => 'Start de installatie'; @override String budgetAmount(Object amount) { @@ -523,255 +526,248 @@ class AppLocalizationsNl extends AppLocalizations { } @override - String get addBudget => 'Add budget'; + String get addBudget => 'Budget toevoegen'; @override String addBudgetForCategory(Object cat) { - return 'Add budget for category $cat'; + return 'Budget voor categorie $cat toevoegen'; } @override - String get addCategory => 'Add category'; + String get addCategory => 'Categorie toevoegen'; @override - String get confirm => 'Confirm'; + String get confirm => 'Bevestigen'; @override - String get step1Of2 => 'Step 1 of 2'; + String get step1Of2 => 'Stap 1 van 2'; @override - String get setupMonthlyBudgets => 'Set up your monthly\nbudgets'; + String get setupMonthlyBudgets => 'Stel je maandelijkse budgetten in'; @override - String get chooseCategoriesForBudget => - 'Choose which categories you want to set a budget for'; + String get chooseCategoriesForBudget => 'Kies categorieën voor je budget'; @override - String get monthlyBudgetTotal => 'Monthly budget total:'; + String get monthlyBudgetTotal => 'Totaal maandelijks budget:'; @override - String get nextStep => 'Next step'; + String get nextStep => 'Volgende stap'; @override - String get continueWithoutBudget => 'Continue without budget'; + String get continueWithoutBudget => 'Doorgaan zonder budget'; @override - String get step2Of2 => 'Step 2 OF 2'; + String get step2Of2 => 'Stap 2 van 2'; @override - String get setLiquidityInMainAccount => - 'Set the liquidity in your main account'; + String get setLiquidityInMainAccount => 'Stel saldo in op hoofdrekening'; @override - String get addMoreAccounts => - 'You\'ll be able to add more accounts within the app.'; + String get addMoreAccounts => 'Je kunt later meer rekeningen toevoegen.'; @override String get liquidityDescription => - 'It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou\'ll be able to add more accounts within the app.'; + 'Dit wordt gebruikt als basis voor inkomsten, uitgaven en vermogen.'; @override - String get mainAccount => 'Main account'; + String get mainAccount => 'Hoofdrekening'; @override - String get setAmount => 'Set amount'; + String get setAmount => 'Bedrag instellen'; @override - String get editIconAndColor => 'Edit icon and color'; + String get editIconAndColor => 'Icoon en kleur bewerken'; @override - String get skipStepOrStartFromZero => - 'Or you can skip this step and start from 0'; + String get skipStepOrStartFromZero => 'Of sla over en begin bij 0'; @override - String get startTrackingExpenses => 'Start tracking your expenses'; + String get startTrackingExpenses => 'Begin met uitgaven bijhouden'; @override - String get startFromZero => 'Start from 0'; + String get startFromZero => 'Begin bij 0'; @override String get importExport => 'Import/Export'; @override - String get importData => 'Import data'; + String get importData => 'Gegevens importeren'; @override - String get importDataDescription => - 'Import a CSV file to update your database'; + String get importDataDescription => 'Importeer CSV om database bij te werken'; @override - String get importMoneyManager => 'Import from Money Manager'; + String get importMoneyManager => 'Importeer van Money Manager'; @override String get importMoneyManagerDescription => - 'Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.'; + 'Importeer CSV van Money Manager (opgeslagen als CSV vanuit XLS).'; @override - String get exportData => 'Export data'; + String get exportData => 'Gegevens exporteren'; @override - String get exportDataDescription => 'Save your data as a CSV file'; + String get exportDataDescription => 'Sla gegevens op als CSV-bestand'; @override - String get warningOverwrite => 'Warning: Data Overwrite'; + String get warningOverwrite => 'Waarschuwing: Overschrijven gegevens'; @override String get warningOverwriteContent => - 'Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.'; + 'Importeren vervangt permanent je huidige gegevens. Maak eerst een back-up.'; @override - String get proceedImport => 'Proceed with Import'; + String get proceedImport => 'Doorgaan met importeren'; @override - String get importSuccess => 'Data imported successfully'; + String get importSuccess => 'Gegevens succesvol geïmporteerd'; @override String exportFailed(Object err) { - return 'Export failed: $err'; + return 'Export mislukt: $err'; } @override String errorExporting(Object tableName) { - return 'Failed to export table: $tableName'; + return 'Tabel exporteren mislukt: $tableName'; } @override - String get errorCsvNotFound => 'CSV file not found.'; + String get errorCsvNotFound => 'CSV-bestand niet gevonden.'; @override - String get errorCsvEmpty => 'The CSV file is empty.'; + String get errorCsvEmpty => 'CSV-bestand is leeg.'; @override String errorCsvExpectedColumn(Object column) { - return 'Missing expected column: $column'; + return 'Verwachte kolom mist: $column'; } @override String errorCsvUnexpectedValue(Object value) { - return 'Found an unexpected value: $value'; + return 'Onverwachte waarde gevonden: $value'; } @override String errorCsvImportGeneral(Object error) { - return 'A general error occurred during CSV import. With error: $error'; + return 'Algemene fout bij CSV-import: $error'; } @override String errorCsvTransactionImport(Object date) { - return 'Failed to import transaction on date: $date'; + return 'Import transactie mislukt op: $date'; } @override String errorCleanDatabase(Object error) { - return 'Failed to clean the database. Reason: $error'; + return 'Database opschonen mislukt: $error'; } @override String errorResetDatabase(Object error) { - return 'Failed to reset the database. Reason: $error'; + return 'Database reset mislukt: $error'; } @override String transactionCount(Object count) { - return '$count transactions'; + return '$count transacties'; } @override - String get uncategorized => 'Uncategorized'; + String get uncategorized => 'Niet gecategoriseerd'; @override - String get noIncomesForSelectedMonth => 'No incomes for the selected month'; + String get noIncomesForSelectedMonth => 'Geen inkomsten voor deze maand'; @override - String get noExpensesForSelectedMonth => 'No expenses for the selected month'; + String get noExpensesForSelectedMonth => 'Geen uitgaven voor deze maand'; @override - String get total => 'Total'; + String get total => 'Totaal'; @override - String get noTransactionsAdded => 'There are no transactions added yet'; + String get noTransactionsAdded => 'Nog geen transacties toegevoegd'; @override String get addTransactionCallToAction => - 'Add a transaction to make this section more appealing'; + 'Voeg een transactie toe om dit overzicht te vullen'; @override String get graphsEmptyState => - 'After you add some transactions, some outstanding graphs will appear here... almost by magic!'; + 'Na het toevoegen van transacties verschijnen hier grafieken... als bij toverslag!'; @override - String get availableLiquidity => 'Available liquidity'; + String get availableLiquidity => 'Beschikbare liquiditeit'; @override - String get vsLastMonth => 'VS last month'; + String get vsLastMonth => 't.o.v. vorige maand'; @override - String get monthlyBalance => 'Monthly balance'; + String get monthlyBalance => 'Maandelijks saldo'; @override - String get currentMonth => 'Current month'; + String get currentMonth => 'Huidige maand'; @override - String get lastMonth => 'Last month'; + String get lastMonth => 'Vorige maand'; @override - String get yourAccounts => 'Your accounts'; + String get yourAccounts => 'Je rekeningen'; @override - String get yourBudgets => 'Your budgets'; + String get yourBudgets => 'Je budgetten'; @override - String get createBudgetToTrack => 'Create a budget to track your spending'; + String get createBudgetToTrack => 'Maak een budget om je uitgaven te volgen'; @override - String get close => 'Close'; + String get close => 'Sluiten'; @override - String get edit => 'Edit'; + String get edit => 'Bewerken'; @override - String get errorDuplicatingTransaction => 'Error duplicating transaction'; + String get errorDuplicatingTransaction => 'Fout bij dupliceren transactie'; @override String transactionCreated(Object transaction) { - return '\"$transaction\" has been created'; + return '\"$transaction\" is aangemaakt'; } @override - String get left => 'Left'; + String get left => 'Over'; @override - String get notEnoughDataForGraph => - 'We are sorry but there is not\nenough data to make the graph...'; + String get notEnoughDataForGraph => 'Niet genoeg data voor de grafiek...'; @override - String get generalSettingsDesc => 'Edit general settings'; + String get generalSettingsDesc => 'Bewerk algemene instellingen'; @override - String get accountsDesc => 'Add or edit your accounts'; + String get accountsDesc => 'Accounts toevoegen of bewerken'; @override - String get categoriesDesc => 'Add/edit categories and subcategories'; + String get categoriesDesc => 'Categorieën en subcategorieën beheren'; @override String get budget => 'Budget'; @override - String get budgetDesc => 'Add or edit your budgets'; + String get budgetDesc => 'Budgetten toevoegen of bewerken'; @override - String get importExportDesc => 'Import or export data'; + String get importExportDesc => 'Gegevens importeren of exporteren'; @override - String get notificationsDesc => 'Manage your notifications settings'; + String get notificationsDesc => 'Meldingen beheren'; @override - String get leaveFeedback => 'Leave a feedback'; + String get leaveFeedback => 'Feedback geven'; @override - String get leaveFeedbackDesc => - 'Complete a small form to report a bug or leave a feedback'; + String get leaveFeedbackDesc => 'Meld een bug of geef feedback'; @override - String get appInfoDesc => 'Learn more about us and the app'; + String get appInfoDesc => 'Meer over ons en de app'; } diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 91a663f6..5b8c46b2 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -2,238 +2,238 @@ "@@locale": "nl", "appName": "Sossoldi", "@appName": { - "description": "The name of the application" + "description": "De naam van de applicatie" }, "dashboard": "Dashboard", - "transactions": "Transactions", + "transactions": "Transacties", "planning": "Planning", - "graphs": "Graphs", - "list": "List", - "categories": "Categories", - "expenses": "Expenses", - "incomes": "Incomes", - "expense": "Expense", - "income": "Income", - "transfer": "Transfer", - "accounts": "Accounts", + "graphs": "Grafieken", + "list": "Lijst", + "categories": "Categorieën", + "expenses": "Uitgaven", + "incomes": "Inkomsten", + "expense": "Uitgave", + "income": "Inkomst", + "transfer": "Overboeking", + "accounts": "Rekeningen", "details": "Details", - "account": "Account", - "category": "Category", - "date": "Date", - "investments": "Investments", - "settings": "Settings", - "notifications": "Notifications", - "settingsDisclaimer": "Open source, built by the community", - "addTransaction": "Add transaction", - "totalBalance": "Total balance", - "netWorth": "Net worth", - "save": "Save", - "cancel": "Cancel", - "success": "Success", + "account": "Rekening", + "category": "Categorie", + "date": "Datum", + "investments": "Investeringen", + "settings": "Instellingen", + "notifications": "Meldingen", + "settingsDisclaimer": "Open source, gebouwd door de community", + "addTransaction": "Transactie toevoegen", + "totalBalance": "Totaal saldo", + "netWorth": "Nettowaarde", + "save": "Opslaan", + "cancel": "Annuleren", + "success": "Succes", "ok": "Ok", - "editingTransaction": "Editing transaction", - "newTransaction": "New transaction", - "updateTransaction": "Update transaction", - "recurringPayments": "Recurring payments", + "editingTransaction": "Transactie bewerken", + "newTransaction": "Nieuwe transactie", + "updateTransaction": "Transactie bijwerken", + "recurringPayments": "Terugkerende betalingen", "interval": "Interval", - "endRepetition": "End repetition", - "never": "Never", - "onADate": "On a date", - "switchDisabled": "Switch is disabled", - "recurringTransactionWarning": "This is a transaction generated by a recurring one: any change will affect this unique transaction.\nTo change all future transactions, or recurrence options, TAP HERE.", - "saveCsvFileFailed": "Cannot save the file here, please create or select a folder in Downloads or Documents. Error: {e}", - "errorPickingFile": "Error picking file. Please ensure you have sufficient permissions. Error: {error}", - "storagePermissionRequired": "Storage permission is required to access your files.", - "importingData": "Importing data...", - "exportingData": "Exporting data...", - "fileSavedTo": "File saved to: {path}", - "dataImportedSuccessfully": "Data imported successfully", - "description": "Description", - "addDescription": "Add description", - "duplicateTransactionTitle": "Duplicate transaction", - "duplicateTransactionContent": "This transaction is already in the list. Do you want to duplicate it? You can then edit the new transaction.", - "duplicate": "Duplicate", - "moreFrequent": "More frequent", - "allCategories": "All categories", - "allAccounts": "All accounts", - "errorOccurred": "Error: {err}", - "selectAccount": "Select Account", - "to": "To:", - "from": "From:", - "recurringTransactionAdded": "Recurring transaction added", - "recurringTransactions": "Recurring transactions", - "addTransactionReminder": "Add transaction reminder", - "privacyPolicyTitle": "Privacy Policy", - "privacyCollectTitle": "What Information Do We Collect?", - "privacyChangesTitle": "Changes to This Privacy Policy", - "contactUsTitle": "Contact us", - "privacyIntro": "Sossoldi is built as an open source app. This service is provided by us at no cost and it is intended for use as is.\nWe are not interested in collecting any personal information. We believe such information is yours and yours alone. We do not store or transmit your personal details, nor do we include any advertising or analytics software that talks to third parties.\n", - "privacyCollectBody": "Sossoldi does not collect any personal information or connect to the internet. Any information that you add in the app exists solely on your device and nowhere else.\n", - "privacyChangesBody": "We may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes.\nThis policy is effective as of 2024-01-01\n", - "contactUsBody": "If you have any questions or suggestions about our Privacy Policy, do not hesitate to contact us at \n", - "collaboratorsTitle": "Collaborators", - "meetTheTeam": "Meet the team", - "teamDescription": "Sossoldi is built and maintained by a passionate open source community. Every feature, fix and idea comes from people like you.", - "wantToContribute": "Want to contribute?", - "contributeDescription": "Open an issue, submit a PR or just say hi on GitHub", + "endRepetition": "Einde herhaling", + "never": "Nooit", + "onADate": "Op een datum", + "switchDisabled": "Schakelaar is uitgeschakeld", + "recurringTransactionWarning": "Dit is een transactie gegenereerd door een terugkerende: elke wijziging heeft alleen invloed op deze unieke transactie.\nOm alle toekomstige transacties of herhalingsopties te wijzigen, TIK HIER.", + "saveCsvFileFailed": "Kan het bestand hier niet opslaan, kies een map in Downloads of Documenten. Fout: {e}", + "errorPickingFile": "Fout bij selecteren bestand. Controleer of je voldoende rechten hebt. Fout: {error}", + "storagePermissionRequired": "Opslagtoegang is vereist om je bestanden te openen.", + "importingData": "Gegevens importeren...", + "exportingData": "Gegevens exporteren...", + "fileSavedTo": "Bestand opgeslagen in: {path}", + "dataImportedSuccessfully": "Gegevens succesvol geïmporteerd", + "description": "Beschrijving", + "addDescription": "Beschrijving toevoegen", + "duplicateTransactionTitle": "Transactie dupliceren", + "duplicateTransactionContent": "Deze transactie staat al in de lijst. Wil je deze dupliceren? Je kunt de nieuwe transactie daarna bewerken.", + "duplicate": "Dupliceren", + "moreFrequent": "Vaker", + "allCategories": "Alle categorieën", + "allAccounts": "Alle rekeningen", + "errorOccurred": "Fout: {err}", + "selectAccount": "Selecteer rekening", + "to": "Naar:", + "from": "Van:", + "recurringTransactionAdded": "Terugkerende transactie toegevoegd", + "recurringTransactions": "Terugkerende transacties", + "addTransactionReminder": "Herinnering transactie toevoegen", + "privacyPolicyTitle": "Privacybeleid", + "privacyCollectTitle": "Welke informatie verzamelen we?", + "privacyChangesTitle": "Wijzigingen in dit privacybeleid", + "contactUsTitle": "Contact", + "privacyIntro": "Sossoldi is gebouwd als een open source app. Deze service wordt kosteloos aangeboden en is bedoeld voor gebruik zoals het is.\nWe zijn niet geïnteresseerd in het verzamelen van persoonlijke informatie. Wij geloven dat deze informatie van jou is en van jou alleen. We slaan je persoonlijke gegevens niet op, verzenden ze niet en gebruiken geen advertentie- of analyse-software van derden.\n", + "privacyCollectBody": "Sossoldi verzamelt geen persoonlijke informatie en maakt geen verbinding met het internet. Alle informatie die je toevoegt, blijft uitsluitend op je apparaat.\n", + "privacyChangesBody": "We kunnen ons privacybeleid van tijd tot tijd bijwerken. We raden je aan deze pagina regelmatig te controleren op wijzigingen.\nDit beleid is effectief vanaf 2024-01-01.\n", + "contactUsBody": "Als je vragen of suggesties hebt over ons privacybeleid, neem dan contact met ons op via \n", + "collaboratorsTitle": "Medewerkers", + "meetTheTeam": "Ontmoet het team", + "teamDescription": "Sossoldi wordt gebouwd en onderhouden door een gepassioneerde open source community. Elke functie en fix komt van mensen zoals jij.", + "wantToContribute": "Wil je bijdragen?", + "contributeDescription": "Open een issue, stuur een PR of zeg hallo op GitHub", "appInfo": "App Info", - "appVersion": "App Version:", - "collaborators": "Collaborators", - "collaboratorsDescription": "See the team behind this app", - "privacyPolicy": "Privacy Policy", - "privacyPolicyDescription": "Read more", - "generalSettings": "General Settings", - "appearance": "Appearance", - "currency": "Currency", - "requireAuthentication": "Require authentication", - "searchForATransaction": "Search for a transaction", - "selectACurrency": "Select a currency", - "search": "Search", - "searchIn": "Search in", - "lastTransactions": "Your last transactions", - "startReconciliation": "Start reconciliation", - "newBalance": "New balance", - "balanceDiscrepancy": "Balance Discrepancy?", - "balanceAdjustmentHint": "Your recorded balance might differ from your bank's statement. Tap below to manually adjust your balance and keep your records accurate.", - "newAccount": "New account", - "editAccount": "Edit account", - "createAccount": "Create account", - "accountName": "Account name", - "name": "Name", - "iconAndColor": "Icon and color", - "chooseColor": "Choose color", - "chooseIcon": "Choose icon", - "done": "Fatto", - "add": "Add", - "setAsMainAccount": "Set as main account", - "countsForNetWorth": "Counts for the net worth", - "deleteAccount": "Delete account", - "initialBalance": "Initial balance", - "currentBalance": "Current balance", - "showLess": "Show less", - "showMore": "Show more", - "addSubcategory": "Add subcategory", - "newCategory": "New category", - "editCategory": "Edit category", - "createCategory": "Create category", - "updateCategory": "Update category", - "categoryName": "Category name", + "appVersion": "App Versie:", + "collaborators": "Medewerkers", + "collaboratorsDescription": "Zie het team achter deze app", + "privacyPolicy": "Privacybeleid", + "privacyPolicyDescription": "Lees meer", + "generalSettings": "Algemene instellingen", + "appearance": "Uiterlijk", + "currency": "Valuta", + "requireAuthentication": "Authenticatie vereist", + "searchForATransaction": "Zoek naar een transactie", + "selectACurrency": "Selecteer een valuta", + "search": "Zoeken", + "searchIn": "Zoeken in", + "lastTransactions": "Je laatste transacties", + "startReconciliation": "Start aansluiting", + "newBalance": "Nieuw saldo", + "balanceDiscrepancy": "Saldo afwijking?", + "balanceAdjustmentHint": "Je geregistreerde saldo kan afwijken van je bankafschrift. Tik hieronder om je saldo handmatig aan te passen.", + "newAccount": "Nieuwe rekening", + "editAccount": "Rekening bewerken", + "createAccount": "Rekening maken", + "accountName": "Rekeningnaam", + "name": "Naam", + "iconAndColor": "Icoon en kleur", + "chooseColor": "Kies kleur", + "chooseIcon": "Kies icoon", + "done": "Gereed", + "add": "Toevoegen", + "setAsMainAccount": "Instellen als hoofdrekening", + "countsForNetWorth": "Telt mee voor nettowaarde", + "deleteAccount": "Rekening verwijderen", + "initialBalance": "Beginsaldo", + "currentBalance": "Huidig saldo", + "showLess": "Minder weergeven", + "showMore": "Meer weergeven", + "addSubcategory": "Subcategorie toevoegen", + "newCategory": "Nieuwe categorie", + "editCategory": "Categorie bewerken", + "createCategory": "Categorie maken", + "updateCategory": "Categorie bijwerken", + "categoryName": "Categorienaam", "type": "Type", - "deleteCategory": "Delete category", - "newSubcategory": "New subcategory", - "editSubcategory": "Edit subcategory", - "createSubcategory": "Create subcategory", - "updateSubcategory": "Update subcategory", - "subcategoryName": "Subcategory name", - "deleteSubcategory": "Delete subcategory", - "subcategory": "Subcategory", - "categoryFirstThenBudget": "Add a category first to set a budget", - "inTheNextDays": "In {next} days", - "monthlyBudget": "Monthly budget", - "manage": "Manage", - "swipeLeftToDelete": "Swipe left to delete", - "yourMonthlyBudgetWillBe": "Your monthly budget will be:", - "saveBudget": "Save budget", - "selectCategoriesToCreateBudget": "Select the categories to create your budget", - "amount": "Amount", - "addCategoryBudget": "Add category budget", - "allCategoriesAdded": "You have already added all available categories.", - "delete": "Delete", - "allRecurringPaymentsHere": "All recurring payments will be displayed here", - "addRecurringPayment": "Add recurring payment", - "seeOlderPayments": "See older payments", - "untilDate": "Until {date}", - "olderPayments": "Older payments", - "categoryNotFound": "Category not found", - "back": "Back", - "onTheDay": "- On the {day} day", - "noMonthlyPaymentHistory": "No monthly payment history", - "noRecurrentPaymentHistory": "No recurrent payment history", - "errorLoadingPayments": "Error loading payments: {error}", - "editRecurringTransaction": "Edit recurring transaction", - "detailsExplanation": "Details (any change will affect only future transactions)", - "dateStart": "Date start", - "planned": "Planned", - "composition": "Composition", - "progress": "Progress", - "noBudgetSet": "There are no budgets set", - "budgetHelpText": "A monthly budget can help you keep track of your expenses and stay within the limits", - "createBudget": "Create budget", - "setUpTheApp": "Set up the app", - "setupDescription": "In a few steps you'll be ready to start keeping\ntrack of your personal finances (almost) like\nMr. Rip.", - "startTheSetup": "Start the setup", + "deleteCategory": "Categorie verwijderen", + "newSubcategory": "Nieuwe subcategorie", + "editSubcategory": "Subcategorie bewerken", + "createSubcategory": "Subcategorie maken", + "updateSubcategory": "Subcategorie bijwerken", + "subcategoryName": "Naam subcategorie", + "deleteSubcategory": "Subcategorie verwijderen", + "subcategory": "Subcategorie", + "categoryFirstThenBudget": "Voeg eerst een categorie toe om een budget in te stellen", + "inTheNextDays": "Over {next} dagen", + "monthlyBudget": "Maandelijks budget", + "manage": "Beheren", + "swipeLeftToDelete": "Veeg naar links om te verwijderen", + "yourMonthlyBudgetWillBe": "Je maandelijkse budget wordt:", + "saveBudget": "Budget opslaan", + "selectCategoriesToCreateBudget": "Selecteer categorieën voor je budget", + "amount": "Bedrag", + "addCategoryBudget": "Categoriebudget toevoegen", + "allCategoriesAdded": "Je hebt alle beschikbare categorieën al toegevoegd.", + "delete": "Verwijderen", + "allRecurringPaymentsHere": "Alle terugkerende betalingen worden hier getoond", + "addRecurringPayment": "Terugkerende betaling toevoegen", + "seeOlderPayments": "Bekijk oudere betalingen", + "untilDate": "Tot {date}", + "olderPayments": "Oudere betalingen", + "categoryNotFound": "Categorie niet gevonden", + "back": "Terug", + "onTheDay": "- Op de {day}e dag", + "noMonthlyPaymentHistory": "Geen maandelijkse betalingsgeschiedenis", + "noRecurrentPaymentHistory": "Geen terugkerende betalingsgeschiedenis", + "errorLoadingPayments": "Fout bij laden betalingen: {error}", + "editRecurringTransaction": "Terugkerende transactie bewerken", + "detailsExplanation": "Details (wijzigingen gelden alleen voor toekomstige transacties)", + "dateStart": "Startdatum", + "planned": "Gepland", + "composition": "Samenstelling", + "progress": "Voortgang", + "noBudgetSet": "Geen budgetten ingesteld", + "budgetHelpText": "Een budget helpt je om je uitgaven onder controle te houden", + "createBudget": "Budget maken", + "setUpTheApp": "App instellen", + "setupDescription": "In een paar stappen ben je klaar om je financiën bij te houden (bijna) zoals Mr. Rip.", + "startTheSetup": "Start de installatie", "budgetAmount": "Budget {amount}€", - "addBudget": "Add budget", - "addBudgetForCategory": "Add budget for category {cat}", - "addCategory": "Add category", - "confirm": "Confirm", - "step1Of2": "Step 1 of 2", - "setupMonthlyBudgets": "Set up your monthly\nbudgets", - "chooseCategoriesForBudget": "Choose which categories you want to set a budget for", - "monthlyBudgetTotal": "Monthly budget total:", - "nextStep": "Next step", - "continueWithoutBudget": "Continue without budget", - "step2Of2": "Step 2 OF 2", - "setLiquidityInMainAccount": "Set the liquidity in your main account", - "addMoreAccounts": "You'll be able to add more accounts within the app.", - "liquidityDescription": "It will be used as a baseline to which you can add income, expenses and calculate your wealth.\nYou'll be able to add more accounts within the app.", - "mainAccount": "Main account", - "setAmount": "Set amount", - "editIconAndColor": "Edit icon and color", - "skipStepOrStartFromZero": "Or you can skip this step and start from 0", - "startTrackingExpenses": "Start tracking your expenses", - "startFromZero": "Start from 0", + "addBudget": "Budget toevoegen", + "addBudgetForCategory": "Budget voor categorie {cat} toevoegen", + "addCategory": "Categorie toevoegen", + "confirm": "Bevestigen", + "step1Of2": "Stap 1 van 2", + "setupMonthlyBudgets": "Stel je maandelijkse budgetten in", + "chooseCategoriesForBudget": "Kies categorieën voor je budget", + "monthlyBudgetTotal": "Totaal maandelijks budget:", + "nextStep": "Volgende stap", + "continueWithoutBudget": "Doorgaan zonder budget", + "step2Of2": "Stap 2 van 2", + "setLiquidityInMainAccount": "Stel saldo in op hoofdrekening", + "addMoreAccounts": "Je kunt later meer rekeningen toevoegen.", + "liquidityDescription": "Dit wordt gebruikt als basis voor inkomsten, uitgaven en vermogen.", + "mainAccount": "Hoofdrekening", + "setAmount": "Bedrag instellen", + "editIconAndColor": "Icoon en kleur bewerken", + "skipStepOrStartFromZero": "Of sla over en begin bij 0", + "startTrackingExpenses": "Begin met uitgaven bijhouden", + "startFromZero": "Begin bij 0", "importExport": "Import/Export", - "importData": "Import data", - "importDataDescription": "Import a CSV file to update your database", - "importMoneyManager": "Import from Money Manager", - "importMoneyManagerDescription": "Import CSV from Money Manager to update your database. The file must be saved as CSV from XLS.", - "exportData": "Export data", - "exportDataDescription": "Save your data as a CSV file", - "warningOverwrite": "Warning: Data Overwrite", - "warningOverwriteContent": "Importing this file will permanently replace your existing data. This action cannot be undone. Ensure you have a backup before proceeding.", - "proceedImport": "Proceed with Import", - "importSuccess": "Data imported successfully", - "exportFailed": "Export failed: {err}", - "errorExporting": "Failed to export table: {tableName}", - "errorCsvNotFound": "CSV file not found.", - "errorCsvEmpty": "The CSV file is empty.", - "errorCsvExpectedColumn": "Missing expected column: {column}", - "errorCsvUnexpectedValue": "Found an unexpected value: {value}", - "errorCsvImportGeneral": "A general error occurred during CSV import. With error: {error}", - "errorCsvTransactionImport": "Failed to import transaction on date: {date}", - "errorCleanDatabase": "Failed to clean the database. Reason: {error}", - "errorResetDatabase": "Failed to reset the database. Reason: {error}", - "transactionCount": "{count} transactions", - "uncategorized": "Uncategorized", - "noIncomesForSelectedMonth": "No incomes for the selected month", - "noExpensesForSelectedMonth": "No expenses for the selected month", - "total": "Total", - "noTransactionsAdded": "There are no transactions added yet", - "addTransactionCallToAction": "Add a transaction to make this section more appealing", - "graphsEmptyState": "After you add some transactions, some outstanding graphs will appear here... almost by magic!", - "availableLiquidity": "Available liquidity", - "vsLastMonth": "VS last month", - "monthlyBalance": "Monthly balance", - "currentMonth": "Current month", - "lastMonth": "Last month", - "yourAccounts": "Your accounts", - "yourBudgets": "Your budgets", - "createBudgetToTrack": "Create a budget to track your spending", - "close": "Close", - "edit": "Edit", - "errorDuplicatingTransaction": "Error duplicating transaction", - "transactionCreated": "\"{transaction}\" has been created", - "left": "Left", - "notEnoughDataForGraph": "We are sorry but there is not\nenough data to make the graph...", - "generalSettingsDesc": "Edit general settings", - "accountsDesc": "Add or edit your accounts", - "categoriesDesc": "Add/edit categories and subcategories", + "importData": "Gegevens importeren", + "importDataDescription": "Importeer CSV om database bij te werken", + "importMoneyManager": "Importeer van Money Manager", + "importMoneyManagerDescription": "Importeer CSV van Money Manager (opgeslagen als CSV vanuit XLS).", + "exportData": "Gegevens exporteren", + "exportDataDescription": "Sla gegevens op als CSV-bestand", + "warningOverwrite": "Waarschuwing: Overschrijven gegevens", + "warningOverwriteContent": "Importeren vervangt permanent je huidige gegevens. Maak eerst een back-up.", + "proceedImport": "Doorgaan met importeren", + "importSuccess": "Gegevens succesvol geïmporteerd", + "exportFailed": "Export mislukt: {err}", + "errorExporting": "Tabel exporteren mislukt: {tableName}", + "errorCsvNotFound": "CSV-bestand niet gevonden.", + "errorCsvEmpty": "CSV-bestand is leeg.", + "errorCsvExpectedColumn": "Verwachte kolom mist: {column}", + "errorCsvUnexpectedValue": "Onverwachte waarde gevonden: {value}", + "errorCsvImportGeneral": "Algemene fout bij CSV-import: {error}", + "errorCsvTransactionImport": "Import transactie mislukt op: {date}", + "errorCleanDatabase": "Database opschonen mislukt: {error}", + "errorResetDatabase": "Database reset mislukt: {error}", + "transactionCount": "{count} transacties", + "uncategorized": "Niet gecategoriseerd", + "noIncomesForSelectedMonth": "Geen inkomsten voor deze maand", + "noExpensesForSelectedMonth": "Geen uitgaven voor deze maand", + "total": "Totaal", + "noTransactionsAdded": "Nog geen transacties toegevoegd", + "addTransactionCallToAction": "Voeg een transactie toe om dit overzicht te vullen", + "graphsEmptyState": "Na het toevoegen van transacties verschijnen hier grafieken... als bij toverslag!", + "availableLiquidity": "Beschikbare liquiditeit", + "vsLastMonth": "t.o.v. vorige maand", + "monthlyBalance": "Maandelijks saldo", + "currentMonth": "Huidige maand", + "lastMonth": "Vorige maand", + "yourAccounts": "Je rekeningen", + "yourBudgets": "Je budgetten", + "createBudgetToTrack": "Maak een budget om je uitgaven te volgen", + "close": "Sluiten", + "edit": "Bewerken", + "errorDuplicatingTransaction": "Fout bij dupliceren transactie", + "transactionCreated": "\"{transaction}\" is aangemaakt", + "left": "Over", + "notEnoughDataForGraph": "Niet genoeg data voor de grafiek...", + "generalSettingsDesc": "Bewerk algemene instellingen", + "accountsDesc": "Accounts toevoegen of bewerken", + "categoriesDesc": "Categorieën en subcategorieën beheren", "budget": "Budget", - "budgetDesc": "Add or edit your budgets", - "importExportDesc": "Import or export data", - "notificationsDesc": "Manage your notifications settings", - "leaveFeedback": "Leave a feedback", - "leaveFeedbackDesc": "Complete a small form to report a bug or leave a feedback", - "appInfoDesc": "Learn more about us and the app" + "budgetDesc": "Budgetten toevoegen of bewerken", + "importExportDesc": "Gegevens importeren of exporteren", + "notificationsDesc": "Meldingen beheren", + "leaveFeedback": "Feedback geven", + "leaveFeedbackDesc": "Meld een bug of geef feedback", + "appInfoDesc": "Meer over ons en de app" } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index a0809f7f..66281014 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -110,9 +110,9 @@ class Launcher extends ConsumerWidget { supportedLocales: [ const Locale('en'), // English const Locale('pt'), // Portuguese - const Locale('es'), // Spanish - const Locale('de'), // German - const Locale('nl'), // Dutch + // const Locale('es'), // Spanish + // const Locale('de'), // German + // const Locale('nl'), // Dutch const Locale('it'), // Italian // Locale('zh'), // Chinese // Locale('hi'), // Hindi